<template>
  <div class="heatmap-page">
    <!-- Canvas渲染中遮罩层 -->
    <div v-if="isCanvasRendering" class="data-loading-overlay">
      <div class="loading-content">
        <div class="loading-spinner"></div>
        <div class="loading-text">渲染中...</div>
      </div>
    </div>
    <div ref="mapRef" class="map"></div>
    
     <!-- 地图类型切换 -->
     <div class="map-type-switcher">
       <div class="switcher-tabs">
         <div 
           class="tab-item" 
           :class="{ active: mapType === 'satellite' }"
           @click="switchMapType('satellite')"
         >
           <i class="tab-icon">🛰️</i>
           <span class="tab-text">风羽图</span>
         </div>
         <div 
           class="tab-item" 
           :class="{ active: mapType === 'heatmap' }"
           @click="switchMapType('heatmap')"
         >
           <i class="tab-icon">🔥</i>
           <span class="tab-text">热力图</span>
         </div>
        <div 
          class="tab-item" 
          :class="{ active: mapType === 'radar' }"
          @click="switchMapType('radar')"
        >
          <i class="tab-icon">📡</i>
          <span class="tab-text">雷达回波</span>
        </div>
        <!-- <div 
           class="tab-item" 
           :class="{ active: mapType === 'particle' }"
           @click="switchMapType('particle')"
         >
           <i class="tab-icon">✨</i>
           <span class="tab-text">粒子图</span>
         </div>-->
       </div>
     </div>
     
     <!-- 地图复位按钮和扫描线速度设置 -->
     <div class="map-controls-group">

       <!-- 扫描线速度设置 - 在风羽图、热力图和雷达回波模式下显示 -->
       <div v-if="mapType === 'satellite' || mapType === 'heatmap' || mapType === 'radar'" class="scan-speed-config">
         <div class="speed-config-title">
           扫描速度
           <button class="speed-config-btn" @click="showSpeedConfig = !showSpeedConfig" :title="showSpeedConfig ? '关闭' : '设置'">
             {{ showSpeedConfig ? '✕' : '⚙️' }}
           </button>
         </div>
         <div v-if="showSpeedConfig" class="speed-config-editor">
           <div class="speed-config-row">
             <input 
               class="speed-config-input" 
               v-model.number="scanSpeed" 
               type="number" 
               step="0.001" 
               min="0.001"
               max="0.1"
               placeholder="0.02"
             />
             <button class="speed-config-save-btn" @click="saveScanSpeed">保存</button>
           </div>
           <div class="speed-config-tip">范围：0.001-0.1</div>
         </div>
       </div>

        <div class="map-reset-button">
         <div class="reset-btn" @click="resetMapToCenter" title="复位到卫星中心点">
           <i class="reset-icon">🎯</i>
           <span class="reset-text">复位</span>
         </div>
       </div>
     </div>
     
     <!-- 数据状态切换 -->
     <div class="data-status-switcher">
       <div class="status-tabs">
         <div 
           class="status-tab-item" 
           :class="{ active: dataStatus === 'realtime' }"
           @click="switchDataStatus('realtime')"
         >
           <i class="status-tab-icon">📊</i>
           <span class="status-tab-text">实时数据</span>
         </div>
         <div 
           class="status-tab-item" 
           :class="{ active: dataStatus === 'history' }"
           @click="switchDataStatus('history')"
         >
           <i class="status-tab-icon">📈</i>
           <span class="status-tab-text">历史数据</span>
         </div>
       </div>
     </div>
     
     <!-- 实时北京时间显示 -->
     <div class="beijing-time-display">
       <div class="time-icon">🕐</div>
       <div class="time-content">
         <div class="time-label">北京时间</div>
         <div class="time-value">{{ beijingTime }}</div>
       </div>
     </div>
     
    
    <!-- 左侧悬浮盒子 -->
    <div class="map-left">
      <!-- 设备状态 -->
      <div class="monitor-panel">
        <DeviceStatus />
        <!-- <el-select class="timeParam" size="small" @change="addArticle" v-model="query.timeParam" placeholder="请选择时间">
           <el-option 
             v-for="item in timeOptions" 
             :key="item.dateTimeString" 
             :label="item.formattedDateTime" 
             :value="item.dateTimeString" 
           />
         </el-select> -->
      </div>
      
      <!-- 报警记录 -->
      <div class="monitor-panel">
        <AlarmTable title="报警记录" device-type="B" :time-options-from-parent="timeOptions" @row-click="handleAlarmRowClick" />
      </div>
    </div>
    
    <!-- 右侧悬浮盒子 -->
    <div class="map-right">
      <!-- 实时风速 -->
      <div class="monitor-panel">
        <RealtimeChart title="实时风速"  chartRef="AA" :speed="realDataTime.speed" :time="realDataTime.time" :lng="realDataTime.lng" :lat="realDataTime.lat" :city="queryParams.city" data-type="A" @showMaxSpeedFlag="handleShowMaxSpeedFlag" @showMaxDirectionFlag="handleShowMaxDirectionFlag" />
      </div>
      
      <!-- 实时风向 -->
      <div class="monitor-panel">
        <RealtimeChart title="实时风向" chartRef="BB" :direction="realDataTime.direction" :time="realDataTime.time" :lng="realDataTime.lng" :lat="realDataTime.lat" :city="queryParams.city" data-type="B" @showMaxDirectionFlag="handleShowMaxDirectionFlag" />
      </div>
    </div>
    
    <!-- 风速图例 - 只在风羽图模式下显示 -->
    <div v-if="mapType === 'satellite'" class="wind-legend">
      <div class="legend-title">
        <span>风羽图例</span>
        <button class="legend-toggle-btn" @click="toggleWindLegendMode" :title="windLegendMode === 'svg' ? '切换到Canvas' : '切换到SVG'">
          {{ windLegendMode === 'svg' ? '🎨' : '🖼️' }}
        </button>
      </div>
      <div class="legend-item" v-for="(range, i) in windLegendRanges" :key="i">
        <img v-if="windLegendMode === 'svg'" class="wind-barb-img" :src="getWindLegendIcon(i)" alt="wind barb" />
        <canvas v-else class="wind-barb-canvas" :ref="el => setLegendCanvasRef(el, i)" :width="24" :height="24"></canvas>
        <span class="legend-text">{{ range.label }} (m/s)</span>
      </div>
    </div>
    
     <!-- 热力图图例 - 只在热力图模式下显示 -->
     <div v-if="mapType === 'heatmap'" class="heatmap-legend">
       <div class="legend-title">热力图图例</div>
       <div class="legend-item">
         <div class="heatmap-color" style="background: linear-gradient(90deg, #0000ff, #0080ff);"></div>
         <span class="legend-text">低风速</span>
       </div>
       <div class="legend-item">
         <div class="heatmap-color" style="background: linear-gradient(90deg, #0080ff, #00ffff);"></div>
         <span class="legend-text">中低风速</span>
       </div>
       <div class="legend-item">
         <div class="heatmap-color" style="background: linear-gradient(90deg, #00ffff, #00ff00);"></div>
         <span class="legend-text">中风速</span>
       </div>
       <div class="legend-item">
         <div class="heatmap-color" style="background: linear-gradient(90deg, #00ff00, #ffff00);"></div>
         <span class="legend-text">高风速</span>
       </div>
       <div class="legend-item">
         <div class="heatmap-color" style="background: linear-gradient(90deg, #ffff00, #ff0000);"></div>
         <span class="legend-text">极高风速</span>
       </div>
     </div>
     
     <!-- 粒子图图例 - 只在粒子图模式下显示 -->
     <div v-if="mapType === 'particle'" class="particle-legend">
       <div class="legend-title">风场图例</div>
       <div class="legend-item">
         <div class="wind-demo" style="background: conic-gradient(from 0deg, #00ff88 0deg, #00ff8880 90deg, transparent 180deg, #00ff8840 270deg, #00ff88 360deg);"></div>
         <span class="legend-text">微风</span>
       </div>
       <div class="legend-item">
         <div class="wind-demo" style="background: conic-gradient(from 0deg, #00aaff 0deg, #00aaff80 90deg, transparent 180deg, #00aaff40 270deg, #00aaff 360deg);"></div>
         <span class="legend-text">轻风</span>
       </div>
       <div class="legend-item">
         <div class="wind-demo" style="background: conic-gradient(from 0deg, #ffaa00 0deg, #ffaa0080 90deg, transparent 180deg, #ffaa0040 270deg, #ffaa00 360deg);"></div>
         <span class="legend-text">中风</span>
       </div>
       <div class="legend-item">
         <div class="wind-demo" style="background: conic-gradient(from 0deg, #ff4400 0deg, #ff440080 90deg, transparent 180deg, #ff440040 270deg, #ff4400 360deg);"></div>
         <span class="legend-text">强风</span>
       </div>
     </div>

    <!-- 径向风速图例 - 只在雷达模式下显示 -->
    <div v-if="mapType === 'radar'" class="heatmap-legend">
      <div class="legend-title">径向风速 (m/s)</div>
      <div class="legend-item">
        <div class="heatmap-color" style="background: #ff0000;"></div>
        <span class="legend-text">40</span>
      </div>
      <div class="legend-item">
        <div class="heatmap-color" style="background: #ff4000;"></div>
        <span class="legend-text">32</span>
      </div>
      <div class="legend-item">
        <div class="heatmap-color" style="background: #ff6000;"></div>
        <span class="legend-text">28</span>
      </div>
      <div class="legend-item">
        <div class="heatmap-color" style="background: #ff8000;"></div>
        <span class="legend-text">24</span>
      </div>
      <div class="legend-item">
        <div class="heatmap-color" style="background: #ffa000;"></div>
        <span class="legend-text">20</span>
      </div>
      <div class="legend-item">
        <div class="heatmap-color" style="background: #ffc000;"></div>
        <span class="legend-text">16</span>
      </div>
      <div class="legend-item">
        <div class="heatmap-color" style="background: #ffe000;"></div>
        <span class="legend-text">12</span>
      </div>
      <div class="legend-item">
        <div class="heatmap-color" style="background: #ffff00;"></div>
        <span class="legend-text">8</span>
      </div>
      <div class="legend-item">
        <div class="heatmap-color" style="background: #ffff80;"></div>
        <span class="legend-text">4</span>
      </div>
      <div class="legend-item">
        <div class="heatmap-color" style="background: #ffffc0;"></div>
        <span class="legend-text">0.5</span>
      </div>
      <div class="legend-item">
        <div class="heatmap-color" style="background: #ffffff;"></div>
        <span class="legend-text">0</span>
      </div>
      <div class="legend-item">
        <div class="heatmap-color" style="background: #c0ffff;"></div>
        <span class="legend-text">-0.5</span>
      </div>
      <div class="legend-item">
        <div class="heatmap-color" style="background: #80ffff;"></div>
        <span class="legend-text">-4</span>
      </div>
      <div class="legend-item">
        <div class="heatmap-color" style="background: #00ffff;"></div>
        <span class="legend-text">-8</span>
      </div>
      <div class="legend-item">
        <div class="heatmap-color" style="background: #00c0ff;"></div>
        <span class="legend-text">-12</span>
      </div>
      <div class="legend-item">
        <div class="heatmap-color" style="background: #0080ff;"></div>
        <span class="legend-text">-16</span>
      </div>
      <div class="legend-item">
        <div class="heatmap-color" style="background: #0040ff;"></div>
        <span class="legend-text">-20</span>
      </div>
      <div class="legend-item">
        <div class="heatmap-color" style="background: #0020ff;"></div>
        <span class="legend-text">-24</span>
      </div>
      <div class="legend-item">
        <div class="heatmap-color" style="background: #0000ff;"></div>
        <span class="legend-text">-28</span>
      </div>
      <div class="legend-item">
        <div class="heatmap-color" style="background: #0000c0;"></div>
        <span class="legend-text">-32</span>
      </div>
      <div class="legend-item">
        <div class="heatmap-color" style="background: #000080;"></div>
        <span class="legend-text">-40</span>
      </div>
    </div>
     
     <!-- 粒子图右下角图例 - 参考热力图图例样式 -->
     <div v-if="mapType === 'particle'" class="particle-corner-legend">
       <div class="corner-legend-title">风场强度</div>
       <div class="corner-legend-content">
         <div class="corner-legend-item">
           <div class="corner-wind-demo" style="background: conic-gradient(from 0deg, #00ff88 0deg, #00ff8880 90deg, transparent 180deg, #00ff8840 270deg, #00ff88 360deg);"></div>
           <span class="corner-legend-text">微风 (0-4 m/s)</span>
         </div>
         <div class="corner-legend-item">
           <div class="corner-wind-demo" style="background: conic-gradient(from 0deg, #00aaff 0deg, #00aaff80 90deg, transparent 180deg, #00aaff40 270deg, #00aaff 360deg);"></div>
           <span class="corner-legend-text">轻风 (4-8 m/s)</span>
         </div>
         <div class="corner-legend-item">
           <div class="corner-wind-demo" style="background: conic-gradient(from 0deg, #ffaa00 0deg, #ffaa0080 90deg, transparent 180deg, #ffaa0040 270deg, #ffaa00 360deg);"></div>
           <span class="corner-legend-text">中风 (8-12 m/s)</span>
         </div>
         <div class="corner-legend-item">
           <div class="corner-wind-demo" style="background: conic-gradient(from 0deg, #ff4400 0deg, #ff440080 90deg, transparent 180deg, #ff440040 270deg, #ff4400 360deg);"></div>
           <span class="corner-legend-text">强风 (12-16 m/s)</span>
         </div>
         <div class="corner-legend-item">
           <div class="corner-wind-demo" style="background: conic-gradient(from 0deg, #ff0000 0deg, #ff000080 90deg, transparent 180deg, #ff000040 270deg, #ff0000 360deg);"></div>
           <span class="corner-legend-text">狂风 (>16 m/s)</span>
         </div>
       </div>
     </div>
  </div>
</template>

<script setup name="index_v1">
import { onMounted, onBeforeUnmount, ref, reactive, toRefs, computed, nextTick, watch } from 'vue'
import { useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import {
  getSheetTest2,
  windDataPPI,
  windDataDBS,
  tableQuery,
  getTableList,
  windLidarInit,
  windLidarInfo,
  ppiLr
} from '@/api/system/user'
import windIcon from '@/assets/wind.png'
import Max from '@/assets/max.png'
import Min from '@/assets/min.png'

import hsIcon from '@/assets/hs.png'
import lanseIcon from '@/assets/lanse.png'
import lsIcon from '@/assets/ls.png'
import redIcon from '@/assets/red.png'
import zsIcon from '@/assets/zs.png'
import satellite from '@/assets/satellite.png'
import flag from '@/assets/flag.png'
import directionIcon from '@/assets/direction.png'

// 导入风羽图例 SVG 图标
import wb00 from '@/assets/windbarbsymbols/wb-00.svg?url'
import wb01 from '@/assets/windbarbsymbols/wb-01.svg?url'
import wb02 from '@/assets/windbarbsymbols/wb-02.svg?url'
import wb03 from '@/assets/windbarbsymbols/wb-03.svg?url'
import wb04 from '@/assets/windbarbsymbols/wb-04.svg?url'
import wb05 from '@/assets/windbarbsymbols/wb-05.svg?url'
import wb06 from '@/assets/windbarbsymbols/wb-06.svg?url'
import wb07 from '@/assets/windbarbsymbols/wb-07.svg?url'
import wb08 from '@/assets/windbarbsymbols/wb-08.svg?url'
import wb09 from '@/assets/windbarbsymbols/wb-09.svg?url'
import wb10 from '@/assets/windbarbsymbols/wb-10.svg?url'
import wb11 from '@/assets/windbarbsymbols/wb-11.svg?url'
import wb12 from '@/assets/windbarbsymbols/wb-12.svg?url'
import wb13 from '@/assets/windbarbsymbols/wb-13.svg?url'

import DeviceStatus from '@/components/DeviceStatus/index.vue'
import RealtimeChart from '@/components/RealtimeChart/index.vue'
import AlarmTable from '@/components/AlarmTable/index.vue'
const AMAP_KEY = '29bd8255ecb5376cc007194d8be07640'
const router = useRouter()

const mapRef = ref(null)
const query = ref({
  pageNum: 1,
  pageSize: 100000,
  timeParam: "",
})
const queryParams= ref({
  pageNum: 3,
  pageSize: 1,
  type: 'PPI',
  city: 'hefei', // 默认城市为合肥
})
const timeOptions= ref([])
const state = reactive({
  realDataTime: {
    time: [],
    speed: [],
    direction: [],
    lng: [],
    lat: []
  }
})
// 本地持久化 key
const LEGEND_EDGES_KEY = 'wind_legend_edges'
const SCAN_SPEED_KEY = 'scan_line_speed'
// 是否显示编辑器
const showLegendEditor = ref(false)
const showSpeedConfig = ref(false)
// 区间边界（内置默认）
const speedBinEdges = ref([0, 4, 8, 12, 16, Infinity])
// 表单编辑用的 4 个中间边界
const formEdges = ref([4, 8, 12, 16])
// 扫描线速度（默认值：0.02）
const scanSpeed = ref(0.02)
const CANVAS_SPEED_STEP = 0.5

// 固定的风羽图例范围（14个等级）
const windLegendRanges = [
  { label: '0 ~ 0.2', min: 0, max: 0.2 },
  { label: '0.3 ~ 1.5', min: 0.3, max: 1.5 },
  { label: '1.6 ~ 3.3', min: 1.6, max: 3.3 },
  { label: '3.4 ~ 5.4', min: 3.4, max: 5.4 },
  { label: '5.5 ~ 7.9', min: 5.5, max: 7.9 },
  { label: '8.0 ~ 10.7', min: 8.0, max: 10.7 },
  { label: '10.8 ~ 13.8', min: 10.8, max: 13.8 },
  { label: '13.9 ~ 17.1', min: 13.9, max: 17.1 },
  { label: '17.2 ~ 20.7', min: 17.2, max: 20.7 },
  { label: '20.8 ~ 24.4', min: 20.8, max: 24.4 },
  { label: '24.5 ~ 28.4', min: 24.5, max: 28.4 },
  { label: '28.5 ~ 32.6', min: 28.5, max: 32.6 },
  { label: '32.7 ~ 36.9', min: 32.7, max: 36.9 },
  { label: '> 36.9', min: 36.9, max: Infinity }
]

// 风羽图例图标数组（对应 wb-00.svg 到 wb-13.svg）
const windLegendIcons = [wb00, wb01, wb02, wb03, wb04, wb05, wb06, wb07, wb08, wb09, wb10, wb11, wb12, wb13]

// 获取风羽图例图标
function getWindLegendIcon(index) {
  if (index >= 0 && index < windLegendIcons.length) {
    return windLegendIcons[index]
  }
  return windLegendIcons[0] // 默认返回第一个图标
}

// 根据风速获取对应的图标索引（0-13）
function getWindBarbIconIndex(speed) {
  const spd = parseFloat(speed) || 0
  
  if (spd <= 0.2) return 0      // wb-00: 0 ~ 0.2
  if (spd <= 1.5) return 1        // wb-01: 0.3 ~ 1.5
  if (spd <= 3.3) return 2        // wb-02: 1.6 ~ 3.3
  if (spd <= 5.4) return 3        // wb-03: 3.4 ~ 5.4
  if (spd <= 7.9) return 4        // wb-04: 5.5 ~ 7.9
  if (spd <= 10.7) return 5       // wb-05: 8.0 ~ 10.7
  if (spd <= 13.8) return 6       // wb-06: 10.8 ~ 13.8
  if (spd <= 17.1) return 7       // wb-07: 13.9 ~ 17.1
  if (spd <= 20.7) return 8       // wb-08: 17.2 ~ 20.7
  if (spd <= 24.4) return 9       // wb-09: 20.8 ~ 24.4
  if (spd <= 28.4) return 10      // wb-10: 24.5 ~ 28.4
  if (spd <= 32.6) return 11      // wb-11: 28.5 ~ 32.6
  if (spd <= 36.9) return 12      // wb-12: 32.7 ~ 36.9
  return 13                        // wb-13: > 36.9
}

// 初始化：从 localStorage 读取
try {
  const saved = localStorage.getItem(LEGEND_EDGES_KEY)
  if (saved) {
    const arr = JSON.parse(saved)
    if (Array.isArray(arr) && arr.length === 4 && arr.every(n => typeof n === 'number' && n >= 0)) {
      formEdges.value = arr
      speedBinEdges.value = [0, ...arr, Infinity]
    }
  }
} catch (e) {
  console.warn('读取风羽图例边界失败:', e)
}

// 初始化：从 localStorage 读取扫描线速度
try {
  const savedSpeed = localStorage.getItem(SCAN_SPEED_KEY)
  if (savedSpeed) {
    const speed = parseFloat(savedSpeed)
    if (!isNaN(speed) && speed >= 0.001 && speed <= 0.1) {
      scanSpeed.value = speed
    }
  }
} catch (e) {
  console.warn('读取扫描线速度失败:', e)
}

// 计算标签
const speedBinLabels = computed(() => {
  const edges = speedBinEdges.value
  const labels = []
  for (let i = 0; i < edges.length - 1; i++) {
    const a = edges[i]
    const b = edges[i + 1]
    labels.push(b === Infinity ? `>${a}` : `${a}-${b}`)
  }
  return labels
})

// 用于风羽图示例的代表风速（取区间中点；最后开区间取 a+2 作为代表）
const legendSpeeds = computed(() => {
  const edges = speedBinEdges.value
  const reps = []
  for (let i = 0; i < edges.length - 1; i++) {
    const a = edges[i]
    const b = edges[i + 1]
    reps.push(b === Infinity ? a + 2 : (a + b) / 2)
  }
  return reps
})

function saveLegendEdges() {
  const [e1, e2, e3, e4] = formEdges.value
  if (!(e1 >= 0 && e1 < e2 && e2 < e3 && e3 < e4)) {
    alert('区间需满足：0 ≤ 边界1 < 边界2 < 边界3 < 边界4')
    return
  }
  speedBinEdges.value = [0, e1, e2, e3, e4, Infinity]
  try {
    localStorage.setItem(LEGEND_EDGES_KEY, JSON.stringify([e1, e2, e3, e4]))
  } catch (e) {
    console.warn('保存风羽图例边界失败:', e)
  }
  // 关闭设置弹层
  showLegendEditor.value = false
  // 重新刷新地图数据以应用最新设置
  try {
    addArticle()
  } catch (err) {
    console.warn('刷新数据失败:', err)
  }
}

// 保存扫描线速度
function saveScanSpeed() {
  const speed = scanSpeed.value
  if (!speed || isNaN(speed) || speed < 0.001 || speed > 0.1) {
    alert('速度值必须在 0.001 - 0.1 范围内')
    return
  }
  try {
    localStorage.setItem(SCAN_SPEED_KEY, String(speed))
    showSpeedConfig.value = false
    // 重新启动扫描线以应用新速度
    if (mapType.value === 'satellite' || mapType.value === 'heatmap') {
      clearSatelliteScan()
      setTimeout(() => {
        initSatelliteScan()
      }, 100)
    } else if (mapType.value === 'radar') {
      // 雷达回波模式：重新启动雷达扫描动画
      removeRadarOverlay()
      setTimeout(() => {
        addRadarOverlay()
      }, 100)
    }
  } catch (e) {
    console.warn('保存扫描线速度失败:', e)
  }
}
const {realDataTime }=toRefs(state)
let map = null
let markers = []
let windMassMarks = null
let cachedData = null // 缓存原始数据
let isDataLoaded = false // 标记数据是否已加载

// 地图类型切换
const mapType = ref('satellite') // 默认风羽图

// 数据状态切换
const dataStatus = ref('realtime') // 默认实时数据

// 实时北京时间
const beijingTime = ref('')
let beijingTimeTimer = null

// 更新北京时间
function updateBeijingTime() {
  const now = new Date()
  // 获取北京时间（UTC+8时区）
  const beijingOffset = 8 * 60 // 北京时区偏移（分钟）
  const utcTime = now.getTime() + (now.getTimezoneOffset() * 60 * 1000)
  const beijingDate = new Date(utcTime + (beijingOffset * 60 * 1000))
  
  const year = beijingDate.getFullYear()
  const month = String(beijingDate.getMonth() + 1).padStart(2, '0')
  const day = String(beijingDate.getDate()).padStart(2, '0')
  const hours = String(beijingDate.getHours()).padStart(2, '0')
  const minutes = String(beijingDate.getMinutes()).padStart(2, '0')
  const seconds = String(beijingDate.getSeconds()).padStart(2, '0')
  beijingTime.value = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
}
let maxSpeedInfoWindow = null // 最大风速信息窗体
let minSpeedInfoWindow = null // 最小风速信息窗体
let customOverlays = [] // 自定义覆盖物数组
let heatmapInstance = null // 热力图实例
let radarHeatmap = null // 雷达回波热力图实例（已弃用，改用多边形覆盖层）
let radarOverlayCanvas = null // 雷达扫描覆盖层
let radarOverlayCtx = null
let radarScanAngle = 0
let radarScanAnimId = null
let radarEchoData = null // 多边形回波强度数据 rings x sectors
const radarRings = 8
const radarSectors = 144
let particleSystem = null // 粒子系统实例
let particleAnimationId = null // 粒子动画ID
let maxSpeedFlagMarker = null // 最大风速旗子标记
let maxDirectionFlagMarker = null // 最大风向图标标记
let ppiTimer = null // PPI数据定时器
let windBatchAnimId = null // 风羽图批量渲染动画ID

// Icon 缓存池，用于复用 Icon 对象，提升性能
const iconCache = new Map()
const massMarkerIconCache = new Map()
// 统一的缩放事件监听器（避免每个 Marker 都监听）
let globalZoomHandler = null
// 缩放更新控制
let zoomUpdateTimer = null // 防抖定时器
let zoomUpdateAnimId = null // requestAnimationFrame ID
let currentZoomLevel = null // 当前缩放级别，避免重复更新
let isZoomUpdating = false // 是否正在更新缩放

// Canvas缓存用于风羽图优化（增加缓存大小以提升性能）
const canvasCache = new Map()
// Canvas 元素池，用于复用 Canvas 元素，避免频繁创建
const canvasPool = new Map() // key: size, value: { canvas, ctx }

// 风羽图例显示模式：'svg' 或 'canvas'
const windLegendMode = ref('canvas')
// Canvas 图例元素的引用
const legendCanvasRefs = ref([])
// Canvas 渲染状态
const isCanvasRendering = ref(false)

// 清除最大值标记
function clearMaxMarkers() {
  if (maxSpeedFlagMarker && map) {
    map.remove(maxSpeedFlagMarker)
    maxSpeedFlagMarker = null
  }
  if (maxDirectionFlagMarker && map) {
    map.remove(maxDirectionFlagMarker)
    maxDirectionFlagMarker = null
  }
}

// 处理报警记录行点击事件
function handleAlarmRowClick(row) {
  console.log('处理报警记录行点击:', row)
  
  // 检查是否有地图数据
  if (!cachedData || !Array.isArray(cachedData)) {
    console.warn('没有地图数据，无法定位')
    return
  }
  
  // 根据坐标查找对应的地图标记
  const targetLng = parseFloat(row.xCoordinate)
  const targetLat = parseFloat(row.yCoordinate)
  
  if (isNaN(targetLng) || isNaN(targetLat)) {
    console.warn('无效的坐标数据')
    return
  }
  
  // 在地图数据中查找匹配的点（允许小的误差）
  const tolerance = 0.0001 // 坐标容差
  const matchingPoint = cachedData.find(point => {
    const pointLng = parseFloat(point.xCoordinate)
    const pointLat = parseFloat(point.yCoordinate)
    
    return Math.abs(pointLng - targetLng) < tolerance && 
           Math.abs(pointLat - targetLat) < tolerance
  })
  
  if (matchingPoint) {
    console.log('找到匹配的地图点:', matchingPoint)
    
    // 清除现有的信息窗体
    clearInfoWindows()
    
    // 创建信息窗体内容
    const infoContent = `
      <div style="padding: 12px; min-width: 250px; background: #fff3cd; border: 2px solid #ffc107; border-radius: 8px; box-shadow: 0 4px 8px rgba(0,0,0,0.2);">
        <h4 style="margin: 0 0 8px 0; color: #856404;">报警记录详情</h4>
        <div style="margin-bottom: 4px;"><strong>风向:</strong> ${matchingPoint.windDirection}°</div>
        <div style="margin-bottom: 4px;"><strong>风速:</strong> ${matchingPoint.windSpeed} m/s</div>
        <div style="margin-bottom: 4px;"><strong>位置:</strong> ${targetLng}, ${targetLat}</div>
        <div style="margin-bottom: 4px;"><strong>时间:</strong> ${row.time || 'N/A'}</div>
        <div style="margin-top: 8px; font-size: 12px; color: #666;">点击报警记录自动定位</div>
        <!-- 箭头指向下方 -->
        <div style="position: absolute; bottom: -15px; left: 50%; transform: translateX(-50%); width: 0; height: 0; border-left: 12px solid transparent; border-right: 12px solid transparent; border-top: 15px solid #ffc107;"></div>
      </div>
    `
    
    // 创建自定义信息窗体覆盖物
    const infoOverlay = createCustomInfoOverlay(
      [targetLng, targetLat], 
      infoContent, 
      'custom-info-window alarm-record'
    )
    
    if (infoOverlay) {
      customOverlays.push(infoOverlay)
      
      // 将地图中心移动到该点
      map.setCenter([targetLng, targetLat])
      
      // 适当放大以便更好地查看
      const currentZoom = map.getZoom()
      if (currentZoom < 15) {
        map.setZoom(15)
      }
    }
  } else {
    console.warn('未找到匹配的地图点')
    // 可以显示一个提示消息
    console.log('该报警记录在地图上没有对应的数据点')
  }
}

function loadAMap() {
  return new Promise((resolve, reject) => {
    if (window.AMap && window.AMap.Marker) {
      window.AMap.plugin(['AMap.HeatMap', 'AMap.MassMarks'], () => {
        resolve(true)
      })
      return
    }
    const cbName = `__amap_cb_${Date.now()}_${Math.floor(Math.random() * 10000)}`
    const url = `https://webapi.amap.com/maps?v=2.0&key=${AMAP_KEY}&callback=${cbName}`
    let settled = false
    window[cbName] = () => {
      settled = true
      window.AMap.plugin(['AMap.HeatMap', 'AMap.MassMarks'], () => {
        resolve(true)
      })
      try {
        delete window[cbName]
      } catch {}
    }
    const script = document.createElement('script')
    script.src = url
    script.async = true
    script.onerror = () => {
      if (!settled) reject(new Error('AMap 脚本加载失败'))
    }
    document.head.appendChild(script)

    const start = Date.now()
    ;(async () => {
      while (!settled && Date.now() - start < 15000) {
        if (window.AMap && window.AMap.Marker) {
          settled = true
          window.AMap.plugin(['AMap.HeatMap', 'AMap.MassMarks'], () => {
            resolve(true)
          })
          try {
            delete window[cbName]
          } catch {}
          return
        }
        await new Promise((r) => setTimeout(r, 100))
      }
      if (!settled) reject(new Error('AMap 加载超时'))
    })()
  })
}

// 生成模拟风向数据
function generateWindData(count = 1125) {
  const centerLng = 117.050972
  const centerLat = 32.390648
  const data = []

  for (let i = 0; i < count; i++) {
    const lng = centerLng + (Math.random() - 0.5) * 0.4
    const lat = centerLat + (Math.random() - 0.5) * 0.3
    const direction = Math.random() * 360 // 风向角度 (0-360度)
    const speed = 5 + Math.random() * 15 // 风速 (5-20 m/s)

    data.push({
      lng,
      lat,
      direction,
      speed
    })
  }
  return data
}

function initMap() {
  map = new window.AMap.Map(mapRef.value, {
    viewMode: '2D',
    zoom: 12,
    center: [centerLng.value, centerLat.value],
    mapStyle: 'amap://styles/satellite', // 默认风羽图
    zooms: [8, 16], // 设置缩放级别范围：最小8级，最大20级
    // 可选的地图样式：

  })

  // 添加风羽图层
  const satelliteLayer = new window.AMap.TileLayer.Satellite()
  map.add(satelliteLayer)

  // 在中心点添加卫星图标
  centerMarker = new window.AMap.Marker({
    position: [centerLng.value, centerLat.value],
    icon: new window.AMap.Icon({
      size: new window.AMap.Size(40, 40),
      image: satellite,
      imageSize: new window.AMap.Size(40, 40)
    }),
    title: '卫星中心点',
    anchor: 'center',
    offset: new window.AMap.Pixel(0, 0)
  })
  map.add(centerMarker)

  // 添加雷达波动态效果
  // 创建雷达波DOM元素
  const radarElement = document.createElement('div')
  radarElement.className = 'radar-overlay'
  radarElement.style.position = 'absolute'
  radarElement.style.width = '200px'
  radarElement.style.height = '200px'
  radarElement.style.pointerEvents = 'none'
  radarElement.style.transform = 'translate(-50%, -50%)'
  radarElement.style.zIndex = '999'

  // 创建雷达波圆形
  const radarCircle = document.createElement('div')
  radarCircle.className = 'radar-circle'
  radarCircle.style.position = 'absolute'
  radarCircle.style.width = '100%'
  radarCircle.style.height = '100%'
  radarCircle.style.border = '2px solid #00ff00'
  radarCircle.style.borderRadius = '50%'
  radarCircle.style.animation = 'radarPulse 2s ease-in-out infinite'
  radarCircle.style.opacity = '0.8'

  // 创建第二个雷达波圆形（延迟动画）
  const radarCircle2 = document.createElement('div')
  radarCircle2.className = 'radar-circle-2'
  radarCircle2.style.position = 'absolute'
  radarCircle2.style.width = '100%'
  radarCircle2.style.height = '100%'
  radarCircle2.style.border = '2px solid #00ff00'
  radarCircle2.style.borderRadius = '50%'
  radarCircle2.style.animation = 'radarPulse 2s ease-in-out infinite 0.5s'
  radarCircle2.style.opacity = '0.6'

  // 创建第三个雷达波圆形（延迟动画）
  const radarCircle3 = document.createElement('div')
  radarCircle3.className = 'radar-circle-3'
  radarCircle3.style.position = 'absolute'
  radarCircle3.style.width = '100%'
  radarCircle3.style.height = '100%'
  radarCircle3.style.border = '2px solid #00ff00'
  radarCircle3.style.borderRadius = '50%'
  radarCircle3.style.animation = 'radarPulse 2s ease-in-out infinite 1s'
  radarCircle3.style.opacity = '0.4'

  radarElement.appendChild(radarCircle)
  radarElement.appendChild(radarCircle2)
  radarElement.appendChild(radarCircle3)

  // 将雷达波元素添加到地图容器
  map.getContainer().appendChild(radarElement)

  // 更新雷达波位置
  const updateRadarPosition = () => {
    const pixel = map.lngLatToContainer([centerLng.value, centerLat.value])
    radarElement.style.left = pixel.x + 'px'
    radarElement.style.top = pixel.y + 'px'
  }

  // 初始位置
  updateRadarPosition()

  // 监听地图事件更新位置
  map.on('moveend', updateRadarPosition)
  map.on('zoomend', updateRadarPosition)

  // 监听地图缩放事件，动态更新风向标记位置
  map.on('zoomend', async () => {
    // 只有在风羽图模式下才更新风向标记位置
    if (mapType.value === 'satellite') {
      await updateWindMarkersPosition()
    }
  })

  // 初始化风羽图扫描线
  initSatelliteScan()
}

// 地图类型切换方法
async function switchMapType(type) {
  // 保存切换前的模式，用于判断是否需要特殊处理
  const previousType = mapType.value
  mapType.value = type
  
  // 先清除所有地图上的标记和覆盖物，避免不同模式间的叠加
  console.log('切换模式：清除所有标记和覆盖物')
  clearMapMarkers()
  clearInfoWindows()
  clearMaxMarkers()
  clearHeatmap()
  clearParticleSystem()
  clearSatelliteScan()
  
  // 根据地图类型设置不同的缩放范围
  if (type === 'heatmap') {
    // 热力图状态：限制缩放范围为 [8, 15]
    map.setZooms([8, 15])
    // 设置缩放级别为14
    map.setZoom(14)
    console.log('热力图模式：设置缩放范围为 [8, 15]，当前缩放级别为 14')
  } else if (type === 'satellite' && previousType === 'heatmap') {
    // 从热力图切换到风羽图：设置缩放级别为12
    map.setZooms([8, 16])
    map.setZoom(12)
    console.log('从热力图切换到风羽图：设置缩放级别为 12')
  } else {
    // 其他状态：恢复默认缩放范围 [8, 16]
    map.setZooms([8, 16])
    console.log(`${type}模式：恢复默认缩放范围为 [8, 16]`)
  }
  
  // 根据地图类型控制扫描线显示
  if (type === 'satellite' || type === 'heatmap') {
    // 风羽图和热力图模式：显示扫描线
    console.log(`${type}模式：显示扫描线`)
    // 延迟一点时间确保清除完成后再显示
    setTimeout(() => {
      initSatelliteScan()
    }, 100)
  } else {
    // 其他模式：隐藏扫描线
    console.log(`${type}模式：隐藏扫描线`)
    clearSatelliteScan()
  }
  
  // 统一使用addArticle函数处理地图类型切换
  await addArticle()
}

// 数据状态切换方法
function switchDataStatus(status) {
  dataStatus.value = status
  
  if (status === 'history') {
    // 跳转到 dashboard 页面
    router.push('/dashboard')
  }
  // 实时数据状态不需要特殊处理，保持当前页面
}

// 地图复位到卫星中心点
function resetMapToCenter() {
  if (!map) return
  
  // 使用动态中心点坐标
  const satelliteCenter = [centerLng.value, centerLat.value]
  
  // 设置地图中心点和缩放级别
  map.setCenter(satelliteCenter)
  map.setZoom(14) // 设置为合适的缩放级别
  
  console.log('地图已复位到卫星中心点:', satelliteCenter)
}

// 清除热力图
function clearHeatmap() {
  if (heatmapInstance) {
    heatmapInstance.setMap(null)
    heatmapInstance = null
  }
  if (radarHeatmap) {
    radarHeatmap.setMap(null)
    radarHeatmap = null
  }
  // 移除雷达扫描覆盖层
  removeRadarOverlay()
}

// 清除粒子图
function clearParticleSystem() {
  if (particleAnimationId) {
    cancelAnimationFrame(particleAnimationId)
    particleAnimationId = null
  }
  if (particleSystem) {
    particleSystem.forEach(particle => {
      if (particle.element && particle.element.parentNode) {
        particle.element.parentNode.removeChild(particle.element)
      }
    })
    particleSystem = []
  }
}
function realTime(data) {
  console.log('realTime called with data:', data);
  console.log('Data length:', data?.length);
  console.log('First item:', data?.[0]);
  
  state.realDataTime.time = data.map(r => r.time)
  state.realDataTime.speed = data.map(r => parseFloat(r.windSpeed) || 0)
  state.realDataTime.direction = data.map(r => parseFloat(r.windDirection) || 0)
  state.realDataTime.lng = data
  state.realDataTime.lat = data.map(r => parseFloat(r.lat ?? r.latitude) || 0)
}
// 添加热力图
async function addHeatmap() {
  // 确保热力图插件已加载
  if (!window.AMap.HeatMap) {
    console.log('正在加载热力图插件...')
    await new Promise((resolve) => {
      window.AMap.plugin('AMap.HeatMap', () => {
        console.log('热力图插件加载完成')
        resolve()
      })
    })
  }
  
  // 等待地图完全加载
  if (!map) {
    console.error('地图实例不存在')
    return
  }
    
    // 获取数据
  const res = await windDataPPI(query.value)
  console.log("222");
  // 处理实时数据
    realTime(res.data.result)    
    if (res.data && res.data.result && Array.isArray(res.data.result)) {
      const data = res.data.result
      console.log('获取到的热力图数据长度:', data.length)
      
      // 处理数据，添加随机时间
      const processedData = data.map(item => {
        let baseTime = query.value.timeParam ? new Date(query.value.timeParam) : new Date()
        if (isNaN(baseTime.getTime())) {
          baseTime = new Date()
        }
        const randomOffset = Math.floor(Math.random() * 301) // 0-300秒随机偏移
        const randomTime = new Date(baseTime.getTime() + randomOffset * 1000)
        
        return {
          ...item,
          randomTime: randomTime.toISOString().slice(0, 19).replace('T', ' ')
        }
      })
            // 获取当前地图的缩放级别
      const currentZoom = map ? map.getZoom() : 12
      const dynamicScale = getDynamicScale(currentZoom)
      // 创建热力图数据点
      const heatmapData = processedData.map(item => {
        // 使用与风羽图相同的坐标处理方式
        const lng = item.xCoordinate * 1
        const lat = item.yCoordinate * 1
        return {
          lng: lng,
          lat: lat,
          count: item.windSpeed*1 || 0 // 使用风速作为热力值
        }
      })      
      // 清除现有热力图
      clearHeatmap()
      
      // 创建热力图
      try {
        
        // 使用高德地图官方推荐的方式创建热力图
        await new Promise((resolve) => {
          window.AMap.plugin('AMap.HeatMap', () => {
            console.log('热力图插件加载完成，开始创建实例')
            
            try {
              // 创建热力图实例
              heatmapInstance = new window.AMap.HeatMap(map, {
                radius: 25, // 热力图半径
                gradient: {
                  0.4: 'blue',    // 低值
                  0.6: 'cyan',    // 中低值
                  0.7: 'lime',    // 中值
                  0.8: 'yellow',  // 中高值
                  1.0: 'red'      // 高值
                },
                opacity: [0.3, 0.8], // 透明度范围
                zIndex: 1000
              })
              
              
              // 检查热力图实例的所有属性和方法
              const methods = []
              for (let prop in heatmapInstance) {
                if (typeof heatmapInstance[prop] === 'function') {
                  methods.push(prop)
                }
              }
              console.log('热力图实例的方法:', methods)
              
              // 设置热力图数据
              const maxValue = Math.max(...heatmapData.map(item => item.count))
              console.log('准备设置热力图数据，最大值:', maxValue)
              
              // 尝试使用构造函数参数方式设置数据
              if (heatmapInstance && heatmapInstance.setMap) {
                // 先设置到地图
                heatmapInstance.setMap(map)
                
                // 尝试设置数据
                if (typeof heatmapInstance.setDataSet === 'function') {
                  heatmapInstance.setDataSet({
                    data: heatmapData,
                    max: maxValue
                  })
                  console.log(maxValue,'maxmaxmaxmax使用setDataSet方法设置热力图数据成功')
                } else if (typeof heatmapInstance.setData === 'function') {
                  heatmapInstance.setData(heatmapData)
                  console.log('使用setData方法设置热力图数据成功')
                } else {
                  console.log('热力图实例已创建并设置到地图，但无法设置数据')
                  console.log('可能需要使用不同的数据格式或方法')
                }
              } else {
                console.error('热力图实例创建失败或缺少setMap方法')
              }
              
            } catch (error) {
              console.error('创建热力图实例时发生错误:', error)
            }
            
            resolve()
          })
        })
        
      } catch (error) {
        console.error('创建热力图时发生错误:', error)
      }
    }

}
async function getEcho() {
  const response = await tableQuery({
    pageNum: 1,
    pageSize: 1,
    type: 'PPILR',
    city: queryParams.value.city, // 添加城市参数
  })
  if (response.data.totalNum) {
    let tableName = response.data.result[0].tableName
    let echoData=await ppiLr({SourceTable:tableName,  pageNum: 1,pageSize: 10000})
    console.log("55555555555ppiLr",echoData);
  }
}
 
// 雷达回波热力图（基于 HeatMap 渲染，使用雷达常见配色梯度）
async function addRadarEcho() {
  let echoData;
  const response = await tableQuery({
    pageNum: 1,
    pageSize: 1,
    type: 'PPILR',
    city: queryParams.value.city, // 添加城市参数
  })
  if (response.data.totalNum) {
    let tableName = response.data.result[0].tableName
    echoData=await ppiLr({SourceTable:tableName,  pageNum: 1,pageSize: 10000})
  }
  if (!map) return
  
  // 使用真实数据生成雷达回波数据
  if (echoData && echoData.data && echoData.data.result) {
    radarEchoData = convertRadialSpeedToRadarData(echoData.data.result)
  } else {
    // 如果没有真实数据，使用模拟数据作为备选
    radarEchoData = generateRadarEchoData(radarRings, radarSectors)
  }
  
  // 清除旧图层并添加多边形覆盖层（含扫描线）
  clearHeatmap()
  addRadarOverlay()
}

// 创建雷达扫描覆盖层（Canvas）
function addRadarOverlay() {
  if (!map) return
  const container = map.getContainer()
  if (!container) return
  
  // 如果已存在，先移除
  removeRadarOverlay()
  
  radarOverlayCanvas = document.createElement('canvas')
  radarOverlayCanvas.className = 'radar-scan-overlay'
  radarOverlayCanvas.style.position = 'absolute'
  radarOverlayCanvas.style.left = '0'
  radarOverlayCanvas.style.top = '0'
  radarOverlayCanvas.style.pointerEvents = 'none'
  radarOverlayCanvas.width = container.clientWidth
  radarOverlayCanvas.height = container.clientHeight
  radarOverlayCtx = radarOverlayCanvas.getContext('2d')
  container.appendChild(radarOverlayCanvas)
  
  // 以创建时的缩放与尺寸作为基准，保证随鼠标缩放时按地图比例缩放
  const baseZoom = map.getZoom()
  const baseRadiusPx = Math.min(container.clientWidth, container.clientHeight) * 0.1
  
  // 存储数据范围信息，用于动态调整雷达图大小
  let dataDistanceRange = null

  function drawFrame() {
    if (!radarOverlayCtx) return
    // 尺寸自适应
    radarOverlayCanvas.width = container.clientWidth
    radarOverlayCanvas.height = container.clientHeight
    const center = map.lngLatToContainer([117.050972, 32.390648])
    // 按地图缩放比例缩放半径（每级缩放按 2 倍比例变化）
    const zoom = map.getZoom()
    const zoomScale = Math.pow(2, zoom - baseZoom)
    
    // 根据数据范围动态调整雷达图大小
    let r = baseRadiusPx * zoomScale
    if (typeof window !== 'undefined' && window.radarDataDistanceRange) {
      // 如果数据范围很大，适当增大雷达图
      const rangeScale = Math.min(2.0, Math.max(0.5, window.radarDataDistanceRange / 500)) // 500km作为基准
      r = r * rangeScale
    }
    
    radarOverlayCtx.clearRect(0, 0, radarOverlayCanvas.width, radarOverlayCanvas.height)
    // 背景网格（同心圆与十字线）
    radarOverlayCtx.save()
    radarOverlayCtx.strokeStyle = 'rgba(0, 198, 255, 0.12)'
    radarOverlayCtx.lineWidth = 1
    for (let i = 1; i <= radarRings; i++) {
      const rr = r * (i / radarRings)
      radarOverlayCtx.beginPath()
      radarOverlayCtx.arc(center.x, center.y, rr, 0, Math.PI * 2)
      radarOverlayCtx.stroke()
    }
    radarOverlayCtx.strokeStyle = 'rgba(0, 198, 255, 0.16)'
    // 水平线
    radarOverlayCtx.beginPath()
    radarOverlayCtx.moveTo(center.x - r, center.y)
    radarOverlayCtx.lineTo(center.x + r, center.y)
    radarOverlayCtx.stroke()
    // 垂直线
    radarOverlayCtx.beginPath()
    radarOverlayCtx.moveTo(center.x, center.y - r)
    radarOverlayCtx.lineTo(center.x, center.y + r)
    radarOverlayCtx.stroke()
    radarOverlayCtx.restore()

    // 绘制多边形回波（按环与扇区）
    if (!radarEchoData) {
      radarEchoData = generateRadarEchoData(radarRings, radarSectors)
    }
    for (let ring = 0; ring < radarEchoData.length; ring++) {
      const innerR = (ring / radarEchoData.length) * r
      const outerR = ((ring + 1) / radarEchoData.length) * r
      for (let sec = 0; sec < radarEchoData[ring].length; sec++) {
        const intensity = radarEchoData[ring][sec]
        // 调整角度：逆时针旋转90度（减去 Math.PI / 2）以修正顺时针多转的90度
        const angleOffset = -Math.PI / 2
        const startA = (sec / radarEchoData[ring].length) * Math.PI * 2 + angleOffset
        const endA = ((sec + 1) / radarEchoData[ring].length) * Math.PI * 2 + angleOffset
        const fillColor = getEchoColor(intensity)
        radarOverlayCtx.beginPath()
        // 外弧
        radarOverlayCtx.moveTo(center.x + innerR * Math.cos(startA), center.y + innerR * Math.sin(startA))
        radarOverlayCtx.arc(center.x, center.y, outerR, startA, endA)
        // 内弧回到起点（逆向）
        radarOverlayCtx.arc(center.x, center.y, innerR, endA, startA, true)
        radarOverlayCtx.closePath()
        radarOverlayCtx.fillStyle = fillColor
        radarOverlayCtx.globalAlpha = 0.40
        radarOverlayCtx.fill()
        // 轮廓线
        radarOverlayCtx.globalAlpha = 1
        radarOverlayCtx.strokeStyle = 'rgba(0,0,0,0.06)'
        radarOverlayCtx.lineWidth = 0.7
        radarOverlayCtx.stroke()
      }
    }
    // 扫描线（调整角度：逆时针旋转90度以修正顺时针多转的90度）
    const angleOffset = -Math.PI / 2
    const adjustedScanAngle = radarScanAngle + angleOffset
    const endX = center.x + r * Math.cos(adjustedScanAngle)
    const endY = center.y + r * Math.sin(adjustedScanAngle)
    radarOverlayCtx.strokeStyle = 'rgba(0,255,0,0.4)'
    radarOverlayCtx.lineWidth = 2
    radarOverlayCtx.beginPath()
    radarOverlayCtx.moveTo(center.x, center.y)
    radarOverlayCtx.lineTo(endX, endY)
    radarOverlayCtx.stroke()
    // 扫描扇形
    radarOverlayCtx.fillStyle = 'rgba(0,255,0,0.04)'
    radarOverlayCtx.beginPath()
    radarOverlayCtx.moveTo(center.x, center.y)
    radarOverlayCtx.arc(center.x, center.y, r, adjustedScanAngle - 0.12, adjustedScanAngle + 0.12)
    radarOverlayCtx.closePath()
    radarOverlayCtx.fill()
    
    // 更新扫描角度（使用可配置的速度值）
    radarScanAngle += scanSpeed.value
    if (radarScanAngle > Math.PI * 2) radarScanAngle = 0
    
    // 移除随机微调代码，保持真实数据稳定性
    // 原来的随机微调会导致扇区颜色闪烁，影响真实数据显示
    
    radarScanAnimId = requestAnimationFrame(drawFrame)
  }
  drawFrame()
  
  // 自适应地图尺寸变化
  const onResize = () => {
    if (!radarOverlayCanvas || !map) return
    radarOverlayCanvas.width = container.clientWidth
    radarOverlayCanvas.height = container.clientHeight
  }
  map.on('zoomend', onResize)
  map.on('moveend', onResize)
}

function removeRadarOverlay() {
  if (radarScanAnimId) {
    cancelAnimationFrame(radarScanAnimId)
    radarScanAnimId = null
  }
  if (radarOverlayCanvas && radarOverlayCanvas.parentNode) {
    radarOverlayCanvas.parentNode.removeChild(radarOverlayCanvas)
  }
  radarOverlayCanvas = null
  radarOverlayCtx = null
}

// 生成模拟回波强度数据（0-7）
function generateRadarEchoData(rings, sectors) {
  const data = []
  for (let r = 0; r < rings; r++) {
    const row = []
    for (let s = 0; s < sectors; s++) {
      // 生成 0-7 范围的强度值，更好地覆盖径向风速的完整范围
      let intensity = Math.random() * 7.0
      // 添加一些变化，使数据更真实
      if (Math.random() > 0.8) intensity = Math.random() * 7.0 // 重新随机化
      row.push(intensity)
    }
    data.push(row)
  }
  return data
}

// 将真实radialSpeed数据转换为雷达回波显示格式
function convertRadialSpeedToRadarData(radialSpeedData) {
  const data = []
  
  // 初始化雷达数据矩阵
  for (let r = 0; r < radarRings; r++) {
    const row = []
    for (let s = 0; s < radarSectors; s++) {
      row.push(0) // 初始化为0
    }
    data.push(row)
  }
  
  // 动态计算最大距离，基于实际数据范围
  const distances = radialSpeedData.map(point => point.distance)
  const minDistance = Math.min(...distances)
  const maxDistance = Math.max(...distances)
  
  // 使用实际数组中的最大值，不进行扩展
  const distanceRange = maxDistance - minDistance
  
  console.log(`实际数据距离范围: ${minDistance}km - ${maxDistance}km`)
  console.log(`数据点总数: ${radialSpeedData.length}`)
  
  // 分析数据分布
  const distanceStats = {
    min: minDistance,
    max: maxDistance,
    range: distanceRange,
    avg: distances.reduce((sum, d) => sum + d, 0) / distances.length,
    median: distances.sort((a, b) => a - b)[Math.floor(distances.length / 2)]
  }
  console.log('距离统计:', distanceStats)
  
  // 处理每个径向速度数据点
  radialSpeedData.forEach(point => {
    const { azimuth, distance, radialSpeed } = point
    
    // 使用实际数据范围进行映射
    const ringIndex = Math.floor(((distance - minDistance) / (maxDistance - minDistance)) * radarRings)
    
    // 将方位角转换为扇区索引 (0-360度 映射到 0-radarSectors)
    const sectorIndex = Math.floor((azimuth / 360) * radarSectors)
    
    // 确保索引在有效范围内
    if (ringIndex >= 0 && ringIndex < radarRings && 
        sectorIndex >= 0 && sectorIndex < radarSectors) {
      
      // 直接使用radialSpeed值，以实际值为准，覆盖就覆盖
      data[ringIndex][sectorIndex] = radialSpeed
      
      // 详细调试信息：显示映射关系
      console.log(`数据点映射:`)
      console.log(`  原始数据: 距离=${distance}km, 方位角=${azimuth}°, 径向速度=${radialSpeed}m/s`)
      console.log(`  映射结果: 环索引=${ringIndex}, 扇区索引=${sectorIndex}`)
      console.log(`  映射计算: ringIndex=floor((${distance}-${minDistance})/(${maxDistance}-${minDistance})*${radarRings})=${ringIndex}`)
      console.log(`  映射计算: sectorIndex=floor(${azimuth}/360*${radarSectors})=${sectorIndex}`)
      console.log(`  角度范围: 扇区${sectorIndex}对应${sectorIndex*10}°-${(sectorIndex+1)*10}°`)
      console.log(`  颜色映射: ${radialSpeed}m/s -> ${getEchoColor(radialSpeed)}`)
      console.log('---')
    } else {
      console.warn(`数据超出范围 - 距离: ${distance}km, 方位角: ${azimuth}°, 径向速度: ${radialSpeed}m/s`)
    }
  })
  
  // 将数据范围信息存储到全局变量，供雷达图绘制使用
  if (typeof window !== 'undefined') {
    window.radarDataDistanceRange = distanceRange
  }
  
  return data
}

// 径向风速强度到颜色映射（-40 到 40 m/s）
function getEchoColor(intensity) {
  // 如果intensity是0（无数据），返回透明色
  if (intensity === 0) return 'rgba(0,0,0,0)'
  
  // 如果intensity是0-7范围的标准化值（来自模拟数据），需要转换
  let windSpeed = intensity
  if (intensity >= 0 && intensity <= 7 && intensity !== 0) {
    // 这是标准化值，需要转换回radialSpeed
    windSpeed = (intensity - 3.5) * 11.43
  }
  
  if (windSpeed >= 40) return '#ff0000'      // 40 m/s - 深红
  if (windSpeed >= 32) return '#ff4000'      // 32 m/s - 红
  if (windSpeed >= 28) return '#ff6000'      // 28 m/s - 红
  if (windSpeed >= 24) return '#ff8000'      // 24 m/s - 红橙
  if (windSpeed >= 20) return '#ffa000'      // 20 m/s - 橙
  if (windSpeed >= 16) return '#ffc000'      // 16 m/s - 橙
  if (windSpeed >= 12) return '#ffe000'      // 12 m/s - 黄橙
  if (windSpeed >= 8) return '#ffff00'       // 8 m/s - 黄
  if (windSpeed >= 4) return '#ffff80'       // 4 m/s - 浅黄
  if (windSpeed >= 0.5) return '#ffffc0'     // 0.5 m/s - 很浅黄
  if (windSpeed >= -0.5) return '#ffffff'    // 0 m/s - 白
  if (windSpeed >= -4) return '#c0ffff'      // -0.5 m/s - 很浅蓝
  if (windSpeed >= -8) return '#80ffff'      // -4 m/s - 浅蓝
  if (windSpeed >= -12) return '#00ffff'     // -8 m/s - 蓝
  if (windSpeed >= -16) return '#00c0ff'     // -12 m/s - 蓝
  if (windSpeed >= -20) return '#0080ff'     // -16 m/s - 深蓝
  if (windSpeed >= -24) return '#0040ff'     // -20 m/s - 深蓝
  if (windSpeed >= -28) return '#0020ff'     // -24 m/s - 深蓝
  if (windSpeed >= -32) return '#0000ff'     // -28 m/s - 深蓝
  if (windSpeed >= -40) return '#0000c0'     // -32 m/s - 深蓝
  return '#000080'                           // -40 m/s - 最深蓝
}

// 添加粒子图 - 天气预报风场旋转效果
async function addParticleSystem() {
  // 等待地图完全加载
  if (!map) {
    console.error('地图实例不存在')
    return
  }
  
  // 获取数据
  const res = await windDataPPI(query.value)
  console.log("333");
  // 处理实时数据
  realTime(res.data.result)
  
  if (res.data && res.data.result && Array.isArray(res.data.result)) {
    const data = res.data.result
    console.log('获取到的粒子图数据长度:', data.length)
    
    // 清除现有粒子系统
    clearParticleSystem()
    
    // 获取当前地图的缩放级别
    const currentZoom = map ? map.getZoom() : 12
    const dynamicScale = getDynamicScale(currentZoom)
    
    // 创建风场网格数据
    const windField = createWindField(data, dynamicScale)
    
    // 创建风场粒子
    const particles = []
    const mapContainer = map.getContainer()
    
    // 创建风场粒子（类似天气预报的风力图）
    const particleCount = Math.min(300, Math.floor(data.length / 3))
    
    for (let i = 0; i < particleCount; i++) {
      // 随机选择起始位置
      const randomIndex = Math.floor(Math.random() * data.length)
      const item = data[randomIndex]
      
      // 使用与风羽图相同的坐标处理方式
      const lng = item.xCoordinate * 1
      const lat = item.yCoordinate * 1
      
      const speed = parseFloat(item.windSpeed) || 0
      const direction = parseFloat(item.windDirection) || 0
      
      // 根据风速确定粒子大小和颜色（使用动态配置）
      const colorConfig = getSpeedColor(speed, true) // 粒子图使用卫星图模式的颜色
      const particleColor = colorConfig.color
      
      // 根据风速确定粒子大小和旋转速度
      let particleSize, particleOpacity, rotationSpeed
      if (speed >= 0 && speed < 4) {
        particleSize = 2 // 减小粒子大小
        particleOpacity = 0.7
        rotationSpeed = 1.5 // 降低旋转速度
      } else if (speed >= 4 && speed < 8) {
        particleSize = 3 // 减小粒子大小
        particleOpacity = 0.8
        rotationSpeed = 2.0 // 降低旋转速度
      } else if (speed >= 8 && speed < 12) {
        particleSize = 4 // 减小粒子大小
        particleOpacity = 0.9
        rotationSpeed = 2.5 // 降低旋转速度
      } else if (speed >= 12 && speed < 16) {
        particleSize = 5 // 减小粒子大小
        particleOpacity = 1.0
        rotationSpeed = 3.0 // 降低旋转速度
      } else {
        particleSize = 6 // 减小粒子大小
        particleOpacity = 1.0
        rotationSpeed = 3.5 // 降低旋转速度
      }
      
      // 创建风场粒子DOM元素
      const particleElement = document.createElement('div')
      particleElement.className = 'wind-field-particle'
      particleElement.style.position = 'absolute'
      particleElement.style.width = particleSize + 'px'
      particleElement.style.height = particleSize + 'px'
      particleElement.style.borderRadius = '50%'
      particleElement.style.background = `conic-gradient(from 0deg, ${particleColor} 0deg, ${particleColor}80 90deg, transparent 180deg, ${particleColor}40 270deg, ${particleColor} 360deg)`
      particleElement.style.opacity = particleOpacity
      particleElement.style.zIndex = '1000'
      particleElement.style.pointerEvents = 'none'
      particleElement.style.transformOrigin = 'center'
      particleElement.style.boxShadow = `0 0 ${particleSize}px ${particleColor}60`
      
      // 添加强烈的旋转动画
      particleElement.style.animation = `windRotate ${rotationSpeed}s linear infinite`
      particleElement.style.animationDelay = Math.random() * 2 + 's'
      
      // 添加到地图容器
      mapContainer.appendChild(particleElement)
      
      // 创建风场粒子对象
      const particle = {
        element: particleElement,
        lng: lng,
        lat: lat,
        speed: speed,
        direction: direction,
        size: particleSize,
        color: particleColor,
        opacity: particleOpacity,
        baseOpacity: particleOpacity,
        rotationSpeed: rotationSpeed,
        // 风场相关属性
        velocity: {
          x: 0,
          y: 0
        },
        trail: [], // 轨迹点
        maxTrailLength: 8,
        age: 0,
        maxAge: 5000 + Math.random() * 3000, // 粒子生命周期
        windField: windField,
        // 风场特有属性（调整版）
        rotationAngle: 0,
        orbitRadius: particleSize * 3, // 增强轨道半径
        orbitSpeed: 0.02, // 降低轨道速度
        centerLng: lng,
        centerLat: lat
      }
      
      particles.push(particle)
    }
    
    particleSystem = particles
    console.log(`创建了 ${particles.length} 个风场粒子`)
    
    // 启动风场旋转动画
    animateWindField()
  }
}

// 创建风场网格数据
function createWindField(data, dynamicScale) {
  const windField = new Map()
  
  data.forEach(item => {
    // 使用与风羽图相同的坐标处理方式
    const lng = item.xCoordinate * 1
    const lat = item.yCoordinate * 1
    
    const speed = parseFloat(item.windSpeed) || 0
    const direction = parseFloat(item.windDirection) || 0
    
    // 将经纬度转换为网格键
    const gridKey = `${Math.round(lng * 1000) / 1000},${Math.round(lat * 1000) / 1000}`
    
    // 计算风向的x,y分量
    const radians = (direction - 90) * Math.PI / 180 // 转换为弧度，-90度是因为风向定义
    const u = speed * Math.cos(radians) // 东西方向分量
    const v = speed * Math.sin(radians) // 南北方向分量
    
    windField.set(gridKey, {
      lng: lng,
      lat: lat,
      speed: speed,
      direction: direction,
      u: u,
      v: v
    })
  })
  
  return windField
}

// 获取指定位置的风场数据（插值）
function getWindAtPosition(lng, lat, windField) {
  // 寻找最近的风场点
  let minDistance = Infinity
  let nearestWind = null
  
  windField.forEach((wind, key) => {
    const distance = Math.sqrt(Math.pow(lng - wind.lng, 2) + Math.pow(lat - wind.lat, 2))
    if (distance < minDistance) {
      minDistance = distance
      nearestWind = wind
    }
  })
  
  return nearestWind || { u: 0, v: 0, speed: 0, direction: 0 }
}

// 风场旋转动画函数 - 单个粒子旋转（增强版）
function animateWindField() {
  if (!particleSystem || particleSystem.length === 0) return
  
  const animate = () => {
    const deltaTime = 16 // 假设60fps，每帧16ms
    
    particleSystem.forEach((particle, index) => {
      if (!particle.element || !particle.element.parentNode) return
      
      try {
        // 获取当前位置的风场数据
        const wind = getWindAtPosition(particle.lng, particle.lat, particle.windField)
        
        // 更新单个粒子的旋转角度（降低旋转速度）
        particle.rotationAngle += particle.orbitSpeed * deltaTime * 0.5 // 降低旋转速度
        
        // 计算轨道运动（增强轨道半径）
        const orbitX = Math.cos(particle.rotationAngle) * particle.orbitRadius * 0.00002 // 增强轨道运动
        const orbitY = Math.sin(particle.rotationAngle) * particle.orbitRadius * 0.00002
        
        // 计算风的影响
        const windScale = 0.00001
        const windU = wind.u * windScale
        const windV = wind.v * windScale
        
        // 更新粒子位置（轨道运动 + 风的影响）
        particle.lng = particle.centerLng + orbitX + windU * 0.2 // 增强风的影响
        particle.lat = particle.centerLat + orbitY + windV * 0.2
        
        // 添加轨迹点
        particle.trail.push({ lng: particle.lng, lat: particle.lat })
        if (particle.trail.length > particle.maxTrailLength) {
          particle.trail.shift()
        }
        
        // 检查粒子是否超出地图边界，如果超出则重新生成
        const bounds = map.getBounds()
        if (particle.lng < bounds.getSouthWest().lng || particle.lng > bounds.getNorthEast().lng || 
            particle.lat < bounds.getSouthWest().lat || particle.lat > bounds.getNorthEast().lat) {
          // 重新生成粒子
          regenerateWindParticle(particle)
          return
        }
        
        // 更新粒子年龄
        particle.age += deltaTime
        if (particle.age > particle.maxAge) {
          // 重新生成粒子
          regenerateWindParticle(particle)
          return
        }
        
        // 计算粒子在地图上的像素位置
        const pixel = map.lngLatToContainer([particle.lng, particle.lat])
        const halfSize = particle.size / 2
        
        // 更新粒子位置
        particle.element.style.left = (pixel.x - halfSize) + 'px'
        particle.element.style.top = (pixel.y - halfSize) + 'px'
        
        // 根据年龄调整透明度
        const ageRatio = particle.age / particle.maxAge
        particle.element.style.opacity = particle.baseOpacity * (1 - ageRatio * 0.2)
        
        // 根据风速调整粒子大小（增强效果）
        const windStrength = Math.sqrt(wind.u * wind.u + wind.v * wind.v)
        const sizeMultiplier = 1 + windStrength * 0.2 // 增强大小变化
        
        // 添加强烈的旋转效果
        const rotationDegrees = (particle.rotationAngle * 180 / Math.PI) % 360
        particle.element.style.transform = `rotate(${rotationDegrees}deg) scale(${sizeMultiplier})`
        
        // 添加脉冲效果（增强）
        const pulse = Math.sin(particle.age * 0.01) * 0.2 + 1 // 增强脉冲效果
        particle.element.style.transform += ` scale(${pulse})`
        
        // 根据风向调整轨道半径（增强）
        particle.orbitRadius = particle.size * 3 + windStrength * 10 // 增强轨道半径
        
        // 添加额外的旋转效果（基于风速）
        const windRotation = windStrength * 0.1
        particle.element.style.transform += ` rotate(${windRotation}deg)`
        
      } catch (error) {
        console.error('更新风场粒子失败:', error)
      }
    })
    
    particleAnimationId = requestAnimationFrame(animate)
  }
  
  animate()
}

// 重新生成风场粒子
function regenerateWindParticle(particle) {
  const bounds = map.getBounds()
  
  // 在地图范围内随机生成位置
  const lng = bounds.getSouthWest().lng + Math.random() * (bounds.getNorthEast().lng - bounds.getSouthWest().lng)
  const lat = bounds.getSouthWest().lat + Math.random() * (bounds.getNorthEast().lat - bounds.getSouthWest().lat)
  
  // 重置粒子属性
  particle.lng = lng
  particle.lat = lat
  particle.centerLng = lng
  particle.centerLat = lat
  particle.velocity = { x: 0, y: 0 }
  particle.trail = []
  particle.age = 0
  particle.maxAge = 5000 + Math.random() * 3000
  particle.rotationAngle = Math.random() * Math.PI * 2
  particle.orbitRadius = particle.size * 2
  particle.element.style.opacity = particle.baseOpacity
  particle.element.style.transform = 'scale(1)'
}

// 重新生成雨滴粒子（保留原函数以防其他地方调用）
function regenerateRainParticle(particle) {
  regenerateWindParticle(particle)
}

// 重新生成粒子（保留原函数以防其他地方调用）
function regenerateParticle(particle) {
  regenerateWindParticle(particle)
}

// 根据缩放级别计算标记大小
function getMarkerSize(zoom) {
  // 基础大小（稍微增大）
  const baseSize = 20
  // 缩放系数：缩放级别越高，标记越大
  const scaleFactor = Math.pow(1.25, zoom - 10) // 以缩放级别10为基准，稍微增加缩放系数
  const size = Math.max(10, Math.min(72, baseSize * scaleFactor)) // 限制在10-72像素之间
  return Math.round(size)
}

// 根据动态速度边界获取颜色配置
function getSpeedColor(speed, isSatelliteMode = false) {
  const edges = speedBinEdges.value
  const speedNum = parseFloat(speed) || 0
  
  // 定义颜色配置 - 卫星图模式使用更亮的颜色
  const colorConfig = [
    { color: isSatelliteMode ? '#00ff88' : '#00ff88', name: '微风' },      // 0-边界1
    { color: isSatelliteMode ? '#00ccff' : '#00aaff', name: '轻风' },      // 边界1-边界2
    { color: isSatelliteMode ? '#ffcc00' : '#ffaa00', name: '中风' },      // 边界2-边界3
    { color: isSatelliteMode ? '#ff6600' : '#ff4400', name: '强风' },      // 边界3-边界4
    { color: isSatelliteMode ? '#ff2200' : '#ff0000', name: '狂风' }       // 边界4+
  ]
  
  // 根据速度找到对应的颜色区间
  for (let i = 0; i < edges.length - 1; i++) {
    const lowerBound = edges[i]
    const upperBound = edges[i + 1]
    
    if (speedNum >= lowerBound && (upperBound === Infinity || speedNum < upperBound)) {
      return colorConfig[i] || colorConfig[0] // 默认返回第一个颜色
    }
  }
  
  // 如果没找到匹配的区间，返回默认颜色
  return colorConfig[0]
}

// 根据风速获取对应的颜色（保持向后兼容）
function getWindBarbColor(speed, isSatelliteMode = false) {
  const colorConfig = getSpeedColor(speed, isSatelliteMode)
  return colorConfig.color
}

function getMassMarkerIcon(point, size, color, legendModeOverride) {
  const legendMode = legendModeOverride || (windLegendMode.value === 'canvas' ? 'canvas' : 'svg')
  const speed = Number(point.speed) || 0

  if (legendMode === 'canvas') {
    const dirKey = directionToAngle(point.direction)
    const cacheKey = `${legendMode}-${color}-${size}-${dirKey}-${speed}`
    if (massMarkerIconCache.has(cacheKey)) {
      return massMarkerIconCache.get(cacheKey)
    }
    const normalizedPoint = {
      ...point,
      direction: dirKey, // 用离散后的角度绘制
      speed
    }
    // MassMarks 图标需要自身带方向，因此 alignUp = false
    const canvasIcon = createCanvasWindBarb(normalizedPoint, size, false)
    if (canvasIcon) {
      massMarkerIconCache.set(cacheKey, canvasIcon)
    }
    return canvasIcon
  }

  // SVG 模式：始终使用预导入的风羽图例 SVG 图标（形状和颜色与图例完全一致）
  const iconIndex = typeof point.iconIndex === 'number'
    ? point.iconIndex
    : getWindBarbIconIndex(speed)
  const svgCacheKey = `${legendMode}-${iconIndex}-${size}`
  if (massMarkerIconCache.has(svgCacheKey)) {
    return massMarkerIconCache.get(svgCacheKey)
  }

  const legendIcon = getWindLegendIcon(iconIndex)
  if (legendIcon) {
    massMarkerIconCache.set(svgCacheKey, legendIcon)
    return legendIcon
  }

  // 如果未找到对应图例，退回到第一个图标，避免空白
  const fallback = getWindLegendIcon(0)
  massMarkerIconCache.set(svgCacheKey, fallback)
  return fallback
}

function generateWindBarbSVGIcon(point, markerSize, color) {
  const speed = Number(point.speed) || 0
  const direction = Number(point.direction) || 0
  const center = markerSize / 2
  const radius = markerSize * 0.42
  const strokeWidth = Math.max(1, markerSize * 0.06)
  const baseAngle = (direction - 90) * Math.PI / 180
  const cosAngle = Math.cos(baseAngle)
  const sinAngle = Math.sin(baseAngle)

  const endX = center + cosAngle * radius
  const endY = center + sinAngle * radius
  let path = `M ${center.toFixed(2)} ${center.toFixed(2)} L ${endX.toFixed(2)} ${endY.toFixed(2)}`

  const barbCount = Math.max(1, Math.floor(speed / 2.5))
  const barbSpacing = radius * 0.2
  const maxBarbLength = radius * 0.38
  const minBarbLength = radius * 0.18

  for (let i = 0; i < barbCount; i++) {
    const distance = radius - (i + 1) * barbSpacing
    if (distance <= strokeWidth) break

    const lengthRatio = barbCount > 1 ? (barbCount - i - 1) / (barbCount - 1) : 1
    const barbLength = minBarbLength + (maxBarbLength - minBarbLength) * lengthRatio

    const baseX = center + cosAngle * distance
    const baseY = center + sinAngle * distance
    const tipX = baseX - sinAngle * barbLength
    const tipY = baseY + cosAngle * barbLength

    path += ` M ${baseX.toFixed(2)} ${baseY.toFixed(2)} L ${tipX.toFixed(2)} ${tipY.toFixed(2)}`
  }

  if (speed >= 10) {
    const triangleSize = markerSize * 0.09
    const triangleBaseX = center + cosAngle * (radius + triangleSize * 0.6)
    const triangleBaseY = center + sinAngle * (radius + triangleSize * 0.6)

    const tri1X = triangleBaseX + (cosAngle - sinAngle) * triangleSize * 0.5
    const tri1Y = triangleBaseY + (sinAngle + cosAngle) * triangleSize * 0.5
    const tri2X = triangleBaseX + (cosAngle + sinAngle) * triangleSize * 0.5
    const tri2Y = triangleBaseY + (sinAngle - cosAngle) * triangleSize * 0.5

    path += ` M ${triangleBaseX.toFixed(2)} ${triangleBaseY.toFixed(2)} L ${tri1X.toFixed(2)} ${tri1Y.toFixed(2)} L ${tri2X.toFixed(2)} ${tri2Y.toFixed(2)} Z`
  }

  const svg = `
    <svg width="${markerSize}" height="${markerSize}" viewBox="0 0 ${markerSize} ${markerSize}" xmlns="http://www.w3.org/2000/svg">
      <path d="${path}" stroke="${color}" stroke-width="${strokeWidth}" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="none" fill="none" />
      <circle cx="${center}" cy="${center}" r="${markerSize * 0.06}" fill="${color}" />
    </svg>
  `
  const encoder = typeof window !== 'undefined' && window.btoa ? window.btoa : btoa
  return `data:image/svg+xml;base64,${encoder(unescape(encodeURIComponent(svg)))}`
}

function buildMassMarksPayload(points, zoom) {
  if (!points || points.length === 0) {
    return { massData: [], styles: [], styleKeys: [], styleMetaMap: new Map() }
  }

  const markerSize = Math.max(14, Math.min(42, Math.round(getMarkerSize(zoom) * 0.8)))
  const isSatelliteMode = mapType?.value === 'satellite'
  const legendMode = windLegendMode.value === 'canvas' ? 'canvas' : 'svg'
  const styleIndexMap = new Map()
  const styleMetaMap = new Map()
  const styleKeys = []
  const styles = []
  const massData = []

  points.forEach((point) => {
    const color = getWindBarbColor(point.speed, isSatelliteMode)
    const rawSpeed = Number(point.speed) || 0
    const roundedDirection = directionToAngle(point.direction)
    const roundedSpeed = legendMode === 'canvas'
      ? Math.round(rawSpeed / CANVAS_SPEED_STEP) * CANVAS_SPEED_STEP
      : Math.round((rawSpeed + Number.EPSILON) * 10) / 10
    const iconIndex = getWindBarbIconIndex(roundedSpeed)
    const styleKey = legendMode === 'canvas'
      ? `${legendMode}-${color}-${roundedDirection}-${roundedSpeed}`
      : `${legendMode}-${iconIndex}-${roundedDirection}-${markerSize}`

    let styleIndex = styleIndexMap.get(styleKey)
    if (styleIndex === undefined) {
      styleIndex = styleKeys.length
      styleKeys.push(styleKey)
      styleIndexMap.set(styleKey, styleIndex)

      const iconPayload = { ...point, direction: roundedDirection, speed: roundedSpeed, iconIndex }
      styleMetaMap.set(styleKey, {
        color,
        legendMode,
        iconPayload,
        rotation: roundedDirection
      })

      const iconUrl = getMassMarkerIcon(iconPayload, markerSize, color, legendMode)
      if (!iconUrl) {
        return
      }
      const styleEntry = {
        url: iconUrl,
        anchor: new window.AMap.Pixel(markerSize / 2, markerSize / 2),
        size: new window.AMap.Size(markerSize, markerSize)
      }
      if (legendMode === 'svg') {
        // SVG图标默认向上（0度），风向角度是顺时针（0度=北，90度=东）
        // 高德地图MassMarks的rotation是顺时针旋转，直接使用风向角度
        // 但需要确保角度正确：如果图标默认向上，风向0度应该旋转0度
        // 注意：高德地图的rotation可能是逆时针，需要取反
        styleEntry.rotation = (360 - roundedDirection) % 360
      }
      styles.push(styleEntry)
    }

    massData.push({
      lnglat: [point.lng, point.lat],
      rawData: point,
      style: styleIndex
    })
  })

  return { massData, styles, styleKeys, styleMetaMap }
}

function handleMassMarkerClick(e) {
  const data = e && e.data
  const point = data?.rawData
  if (!point || !map) {
    return
  }

  clearInfoWindows()
  const infoWindow = new window.AMap.InfoWindow({
    content: `
      <div style="padding: 8px;">
        <div><strong>风向:</strong> ${point.direction ?? 'N/A'}°</div>
        <div><strong>风速:</strong> ${point.speed ?? 'N/A'} m/s</div>
        <div><strong>位置:</strong> ${point.lng?.toFixed?.(4) ?? point.lng}, ${point.lat?.toFixed?.(4) ?? point.lat}</div>
        <div><strong>时间:</strong> ${point.time || point.dateTimeString || 'N/A'}</div>
      </div>
    `,
    offset: new window.AMap.Pixel(0, -20)
  })

  const position = data.lnglat || [point.lng, point.lat]
  infoWindow.open(map, position)
}

function normalizeWindPoints(data) {
  if (!Array.isArray(data)) {
    return []
  }

  return data.reduce((result, point) => {
    const lng = Number(point.lng ?? point.longitude ?? point.xCoordinate)
    const lat = Number(point.lat ?? point.latitude ?? point.yCoordinate)
    if (isNaN(lng) || isNaN(lat)) {
      return result
    }

    const speed = Number(point.speed ?? point.windSpeed)
    const direction = Number(point.direction ?? point.windDirection ?? 0)

    result.push({
      ...point,
      lng,
      lat,
      speed,
      direction
    })
    return result
  }, [])
}

// 切换风羽图例显示模式
function toggleWindLegendMode() {
  windLegendMode.value = windLegendMode.value === 'svg' ? 'canvas' : 'svg'
  massMarkerIconCache.clear()
  // 切换到 Canvas 模式时，等待 DOM 更新后绘制图例
  if (windLegendMode.value === 'canvas') {
    // 显示渲染中提示
    isCanvasRendering.value = true
    nextTick(() => {
      drawLegendCanvases()
    })
  } else {
    isCanvasRendering.value = false
  }
  // 重新渲染地图上的风羽图标记点
  if (mapType.value === 'satellite' && map) {
    // 清除现有标记
    clearMapMarkers()
    clearInfoWindows()
    // 显示渲染中提示（如果切换到Canvas模式）
    if (windLegendMode.value === 'canvas') {
      isCanvasRendering.value = true
    }
    // 重新渲染标记点
    nextTick(() => {
      updateWindMarkersPosition()
    })
  }
}

// 设置 Canvas ref
function setLegendCanvasRef(el, index) {
  if (el) {
    legendCanvasRefs.value[index] = el
    // 如果当前是 Canvas 模式，立即绘制
    if (windLegendMode.value === 'canvas') {
      nextTick(() => {
        drawLegendCanvas(index, el)
      })
    }
  }
}

// 绘制单个 Canvas 图例
function drawLegendCanvas(index, canvas) {
  if (!canvas) return
  
  const range = windLegendRanges[index]
  if (!range) return
  
  // 使用区间的中间值作为风速
  const speed = range.max === Infinity ? range.min + 10 : (range.min + range.max) / 2
  const direction = 0 // 图例中风向统一为 0 度（向上）
  const markerSize = 24
  const isSatelliteMode = mapType.value === 'satellite'
  
  const ctx = canvas.getContext('2d')
  if (!ctx) return
  
  // 设置canvas尺寸
  canvas.width = markerSize
  canvas.height = markerSize
  
  // 根据风速确定颜色
  const color = getWindBarbColor(speed, isSatelliteMode)
  
  // 绘制风羽图
  drawWindBarb(ctx, markerSize, direction, speed, color)
}

// 绘制所有 Canvas 图例
function drawLegendCanvases() {
  // 使用 requestAnimationFrame 确保在下一帧绘制
  requestAnimationFrame(() => {
    const totalCount = legendCanvasRefs.value.filter(c => c).length
    
    if (totalCount === 0) {
      // 如果没有Canvas元素，等待一下再检查（可能DOM还没更新）
      setTimeout(() => {
        const newTotalCount = legendCanvasRefs.value.filter(c => c).length
        if (newTotalCount > 0) {
          // 重新绘制
          drawLegendCanvases()
        } else {
          // 如果还是没有Canvas元素，可能是数据为空或DOM未更新
          // 检查是否有数据，如果没有数据则隐藏提示
          if (!isDataLoaded || !cachedData) {
            isCanvasRendering.value = false
          }
        }
      }, 100)
      return
    }
    
    // 绘制所有Canvas（不隐藏提示，等待地图标记点渲染完成）
    legendCanvasRefs.value.forEach((canvas, index) => {
      if (canvas) {
        drawLegendCanvas(index, canvas)
      }
    })
  })
}

// 监听 mapType 变化，如果切换到卫星图模式且是 Canvas 模式，重新绘制
watch(() => mapType, () => {
  if (mapType.value === 'satellite' && windLegendMode.value === 'canvas') {
    // 显示渲染中提示
    isCanvasRendering.value = true
    nextTick(() => {
      drawLegendCanvases()
    })
  }
})

// 获取或创建 Canvas 元素和上下文（从池中复用）
function getCanvasFromPool(size) {
  if (!canvasPool.has(size)) {
    const canvas = document.createElement('canvas')
    canvas.width = size
    canvas.height = size
    const ctx = canvas.getContext('2d', { 
      willReadFrequently: false, // 优化：不会频繁读取像素
      alpha: true // 需要透明度
    })
    canvasPool.set(size, { canvas, ctx })
  }
  return canvasPool.get(size)
}

// 创建Canvas风羽图（优化版本 - 使用 Canvas 池和更高效的转换）
function createCanvasWindBarb(windPoint, markerSize, alignUp = true) {
  const speed = parseFloat(windPoint.speed) || 0
  const direction = windPoint.direction
  const isSatelliteMode = mapType.value === 'satellite'
  
  // 创建缓存键（包含 alignUp 参数）
  const cacheKey = `${markerSize}_${speed}_${direction}_${isSatelliteMode}_${alignUp}`
  
  // 检查缓存
  if (canvasCache.has(cacheKey)) {
    return canvasCache.get(cacheKey)
  }
  
  // 从池中获取 Canvas 元素和上下文（复用，避免频繁创建）
  const { canvas, ctx } = getCanvasFromPool(markerSize)
  
  // 根据风速确定颜色（针对卫星图背景优化）
  const color = getWindBarbColor(speed, isSatelliteMode)
  
  // 绘制风羽图
  drawWindBarb(ctx, markerSize, direction, speed, color, alignUp)
  
  // 使用更高效的图像格式：image/png（平衡质量和性能）
  const dataURL = canvas.toDataURL('image/png')
  
  // 使用 LRU 策略：当缓存超过 500 时，删除最旧的 100 个
  if (canvasCache.size > 500) {
    // 删除最旧的 100 个缓存项
    const keysToDelete = Array.from(canvasCache.keys()).slice(0, 100)
    keysToDelete.forEach(key => canvasCache.delete(key))
  }
  canvasCache.set(cacheKey, dataURL)
  
  return dataURL
}

// 绘制风羽图的核心函数（极简版本 - 最大化性能）
// alignUp = true: 始终绘制为"向上"，再由外部旋转（普通 Marker）
// alignUp = false: 按照 directionToAngle 直接绘制为真实风向（用于 MassMarks）
function drawWindBarb(ctx, size, direction, speed, color, alignUp = true) {
  // 优化：预先计算常量，避免重复计算
  const halfSize = size * 0.5
  const radius = size * 0.4
  const centerSize = size * 0.08
  const halfCenterSize = centerSize * 0.5
  
  // 清除画布（使用 fillRect 可能更快，但需要先设置背景色，所以保持 clearRect）
  ctx.clearRect(0, 0, size, size)
  
  // 计算风向角度
  // alignUp 为 true 时：统一绘制为"向上"，再由 Marker.angle 控制方向（与之前逻辑一致）
  // alignUp 为 false 时：直接根据风向绘制，用于 MassMarks 图标自身带方向
  const baseDeg = alignUp ? -90 : (directionToAngle(direction) - 90)
  const angle = baseDeg * 0.017453292519943295 // Math.PI / 180 的预计算值
  const cosAngle = Math.cos(angle)
  const sinAngle = Math.sin(angle)
  
  // 设置绘制样式（一次性设置，使用纯色）
  ctx.strokeStyle = color
  ctx.fillStyle = color
  ctx.lineWidth = size * 0.045454545454545456 // 1/22 的预计算值，但至少为 1.0
  if (ctx.lineWidth < 1.0) ctx.lineWidth = 1.0
  ctx.lineCap = 'round'
  
  // 计算主线条终点（与SVG版本一致）
  const endX = halfSize + cosAngle * radius
  const endY = halfSize + sinAngle * radius
  
  // 根据风速计算风羽数量（与SVG版本一致：Math.floor(speed / 2.5)）
  const barbCount = Math.floor(speed / 2.5)
  
  // 一次性绘制所有线条（主线条 + 风羽）
  ctx.beginPath()
  ctx.setLineDash([])
  
  // 主线条
  ctx.moveTo(halfSize, halfSize)
  ctx.lineTo(endX, endY)
  
  // 风羽（与SVG版本保持一致的计算方式，但保留长度渐变效果）
  if (barbCount > 0) {
    const maxBarbLength = radius * 0.3 // 第一个风羽的最大长度（与SVG一致）
    const minBarbLength = radius * 0.15 // 最后一个风羽的最小长度
    const barbSpacing = radius * 0.15 // 固定间距（与SVG版本一致：radius * 0.15）
    
    // 使用与SVG版本相同的循环方式：for (let i = 0; i < barbCount; i++)
    for (let i = 0; i < barbCount; i++) {
      const barbDistance = radius - (i + 1) * barbSpacing
      if (barbDistance <= 0) break
      
      // 计算当前风羽的长度（从最长到最短线性递减）
      const lengthRatio = barbCount > 1 ? (barbCount - i - 1) / (barbCount - 1) : 1
      const currentBarbLength = minBarbLength + (maxBarbLength - minBarbLength) * lengthRatio
      
      const barbX = halfSize + cosAngle * barbDistance
      const barbY = halfSize + sinAngle * barbDistance
      
      // 计算当前风羽的垂直向量（与SVG版本一致：-sin(angle) 和 cos(angle)）
      const perpX = -sinAngle * currentBarbLength
      const perpY = cosAngle * currentBarbLength
      
      ctx.moveTo(barbX, barbY)
      ctx.lineTo(barbX + perpX, barbY + perpY)
    }
  }
  
  // 一次性绘制所有线条
  ctx.stroke()
  
  // 绘制中心点（使用fillRect代替arc，更快）
  ctx.fillRect(halfSize - halfCenterSize, halfSize - halfCenterSize, centerSize, centerSize)
  
  // 强风标记（简化：使用小矩形代替三角形）
  if (speed >= 10) {
    const markerOffset = radius + halfCenterSize
    const markerX = halfSize + cosAngle * markerOffset - halfCenterSize
    const markerY = halfSize + sinAngle * markerOffset - halfCenterSize
    ctx.fillRect(markerX, markerY, centerSize, centerSize)
  }
}

// 根据direction角度转换为对应的angle值（与之前版本保持一致）
function directionToAngle(direction) {
  // 确保direction在0-360范围内
  let dir = direction % 360
  if (dir < 0) dir += 360
  
  // 根据direction的角度范围返回对应的angle值（8个主方向）
  if ((dir >= 337.5 && dir <= 360) || (dir >= 0 && dir < 22.5)) {
    return 0
  } else if (dir >= 22.5 && dir < 67.5) {
    return 45
  } else if (dir >= 67.5 && dir < 112.5) {
    return 90
  } else if (dir >= 112.5 && dir < 157.5) {
    return 135
  } else if (dir >= 157.5 && dir < 202.5) {
    return 180
  } else if (dir >= 202.5 && dir < 247.5) {
    return 225
  } else if (dir >= 247.5 && dir < 292.5) {
    return 270
  } else if (dir >= 292.5 && dir < 337.5) {
    return 315
  }
  return 0 // 默认值
}

// 获取缓存的 Icon 对象，避免重复创建
function getCachedIcon(iconImage, size) {
  const key = `${iconImage}_${size}`
  if (!iconCache.has(key)) {
    iconCache.set(key, new window.AMap.Icon({
      size: new window.AMap.Size(size, size),
      image: iconImage,
      imageSize: new window.AMap.Size(size, size)
    }))
  }
  return iconCache.get(key)
}

// 创建带飘动效果的风向标记
function createFloatingWindMarker(windPoint, markerSize, iconImage) {
  const halfSize = markerSize / 2
  
  // 使用缓存的 Icon 对象，提升性能
  const icon = getCachedIcon(iconImage, markerSize)
  
  // 创建标准的 AMap.Marker
  const marker = new window.AMap.Marker({
    position: [windPoint.lng, windPoint.lat],
    icon: icon,
    angle: directionToAngle(windPoint.direction), // 根据风向旋转图标（使用新的角度转换规则）
    offset: new window.AMap.Pixel(-halfSize, -halfSize),
    zIndex: 1000
  })
  
  // 添加飘动动画效果 - 通过CSS类实现
  marker.on('add', () => {
    // 使用 setTimeout 确保 DOM 元素已渲染
    setTimeout(() => {
      try {
        // 尝试获取 Marker 的 DOM 元素
        const extData = marker.getExtData()
        const markerId = extData?.markerId || `wind-marker-${Date.now()}-${Math.random()}`
        marker.setExtData({ markerId, windDirection: windPoint.direction })
        
        // 通过查找包含当前图标的 DOM 元素来添加动画
        const mapContainer = map.getContainer()
        const markerElements = mapContainer.querySelectorAll('.amap-marker img')
        markerElements.forEach((img) => {
          if (img.src === iconImage || img.src.includes(iconImage.split('/').pop())) {
            const container = img.closest('.amap-marker')
            if (container && !container.classList.contains('floating-wind-marker')) {
              container.classList.add('floating-wind-marker')
              container.style.setProperty('--wind-direction', `${directionToAngle(windPoint.direction)}deg`)
              container.style.animation = 'windFloat 3s ease-in-out infinite'
              container.style.transformOrigin = 'center center'
            }
          }
        })
      } catch (error) {
        console.warn('添加风向标记动画失败:', error)
      }
    }, 100)
  })
  
  // 不再在每个 Marker 上监听缩放事件，改为统一监听（在 addWindMarkers 中处理）
  // 这样可以避免创建大量事件监听器，提升性能
  
  return marker
}

// 创建标准风向标记（回退方案）
function createStandardWindMarker(windPoint, markerSize, iconImage) {
  const halfSize = markerSize / 2
  
  // 使用缓存的 Icon 对象
  const icon = getCachedIcon(iconImage, markerSize)
  
  return new window.AMap.Marker({
    position: [windPoint.lng, windPoint.lat],
    icon: icon,
    angle: directionToAngle(windPoint.direction), // 根据风向旋转图标（使用新的角度转换规则）
    offset: new window.AMap.Pixel(-halfSize, -halfSize)
  })
}

// 批量更新标记图标大小（使用 requestAnimationFrame 分批更新）
function updateMarkersIconSizeBatch(newSize, newHalfSize, visibleMarkers) {
  if (visibleMarkers.length === 0) {
    isZoomUpdating = false
    return
  }

  // 每批更新 30 个标记，减少每批处理量以提升流畅度
  const batchSize = 30
  let batchIndex = 0

  const updateBatch = () => {
    const start = batchIndex * batchSize
    const end = Math.min(start + batchSize, visibleMarkers.length)

    if (start >= visibleMarkers.length) {
      // 所有批次更新完成
      isZoomUpdating = false
      return
    }

    // 更新当前批次的标记
    for (let i = start; i < end; i++) {
      const marker = visibleMarkers[i]
      if (!marker || !marker.getMap()) continue

      try {
        const extData = marker.getExtData()
        const windPoint = extData?.windPoint
        if (!windPoint) continue

        // 根据当前模式决定使用 SVG 还是 Canvas
        let newIconImage = null
        if (windLegendMode.value === 'canvas') {
          // Canvas 模式：重新生成 Canvas 图标
          newIconImage = createCanvasWindBarb(windPoint, newSize)
        } else {
          // SVG 模式：使用原始图标
          const speed = parseFloat(windPoint.speed) || 0
          const iconIndex = getWindBarbIconIndex(speed)
          newIconImage = getWindLegendIcon(iconIndex)
        }

        if (newIconImage) {
          // 使用缓存的 Icon 对象
          const newIcon = getCachedIcon(newIconImage, newSize)
          marker.setIcon(newIcon)
          marker.setOffset(new window.AMap.Pixel(-newHalfSize, -newHalfSize))
        }
      } catch (error) {
        console.warn('更新标记图标失败:', error)
      }
    }

    batchIndex++
    // 继续处理下一批
    zoomUpdateAnimId = requestAnimationFrame(updateBatch)
  }

  // 开始批量更新
  zoomUpdateAnimId = requestAnimationFrame(updateBatch)
}

// 设置统一的缩放事件监听器，避免每个 Marker 都监听
function setupGlobalZoomHandler() {
  // 如果已经设置了，先移除旧的监听器
  if (globalZoomHandler && map) {
    map.off('zoomchange', globalZoomHandler)
    map.off('zoomend', globalZoomHandler)
  }
  
  // 创建统一的缩放事件处理函数（使用防抖和节流优化）
  globalZoomHandler = () => {
    if (!map || markers.length === 0 || isZoomUpdating) return

    const currentZoom = map.getZoom()

    // 如果缩放级别没有变化，跳过更新
    if (currentZoomLevel === currentZoom) return

    // 清除之前的定时器和动画
    if (zoomUpdateTimer) {
      clearTimeout(zoomUpdateTimer)
      zoomUpdateTimer = null
    }
    if (zoomUpdateAnimId) {
      cancelAnimationFrame(zoomUpdateAnimId)
      zoomUpdateAnimId = null
    }

    // 使用防抖：在缩放结束后再更新（延迟 300ms，给用户更多时间完成缩放操作）
    zoomUpdateTimer = setTimeout(() => {
      if (!map || markers.length === 0) return

      const finalZoom = map.getZoom()
      const finalSize = getMarkerSize(finalZoom)
      const finalHalfSize = finalSize / 2

      // 如果缩放级别没有变化，跳过更新
      if (currentZoomLevel === finalZoom) return

      currentZoomLevel = finalZoom
      isZoomUpdating = true

      // 只更新可见区域内的标记，提升性能
      try {
        const bounds = map.getBounds()
        if (!bounds) {
          isZoomUpdating = false
          return
        }

        // 优化：使用更高效的过滤方式，减少计算量
        const sw = bounds.getSouthWest()
        const ne = bounds.getNorthEast()
        const minLng = sw.getLng()
        const maxLng = ne.getLng()
        const minLat = sw.getLat()
        const maxLat = ne.getLat()

        // 快速过滤可见标记（避免调用 bounds.contains，性能更好）
        const visibleMarkers = []
        for (let i = 0; i < markers.length; i++) {
          const marker = markers[i]
          if (!marker || !marker.getMap()) continue

          try {
            const pos = marker.getPosition()
            if (pos && pos.getLng && pos.getLat) {
              const lng = pos.getLng()
              const lat = pos.getLat()
              if (lng >= minLng && lng <= maxLng && lat >= minLat && lat <= maxLat) {
                visibleMarkers.push(marker)
              }
            }
          } catch (error) {
            // 忽略错误，继续处理下一个标记
          }
        }

        // 批量更新可见标记
        updateMarkersIconSizeBatch(finalSize, finalHalfSize, visibleMarkers)
      } catch (error) {
        console.warn('计算可见区域失败:', error)
        isZoomUpdating = false
      }

      zoomUpdateTimer = null
    }, 300) // 防抖延迟增加到 300ms，减少更新频率
  }

  // 在地图上监听缩放事件（使用 zoomend 事件，在缩放结束后触发，性能更好）
  if (map) {
    // 只监听 zoomend 事件，在缩放完全结束后才更新，避免缩放过程中的卡顿
    map.on('zoomend', globalZoomHandler)
    // 监听 zoomchange 用于取消正在进行的更新（但不触发新更新）
    map.on('zoomchange', () => {
      // 缩放过程中，取消之前的更新请求，避免在缩放过程中更新
      if (zoomUpdateTimer) {
        clearTimeout(zoomUpdateTimer)
        zoomUpdateTimer = null
      }
      if (zoomUpdateAnimId) {
        cancelAnimationFrame(zoomUpdateAnimId)
        zoomUpdateAnimId = null
      }
      // 重置更新状态，允许缩放结束后重新更新
      isZoomUpdating = false
    })
  }
}

function createWindMarker(windPoint, zoom = 11, iconType = 'normal') {
  const markerSize = getMarkerSize(zoom)
  const halfSize = markerSize / 2
  
  let iconImage
  
  // 根据 windLegendMode 决定使用 SVG 还是 Canvas
  if (windLegendMode.value === 'canvas') {
    // 使用 Canvas 绘制
    iconImage = createCanvasWindBarb(windPoint, markerSize)
    if (!iconImage) {
      console.error('Canvas 风羽图生成失败，图标类型:', iconType)
      return null
    }
  } else {
    // 使用 SVG 图标
    const speed = parseFloat(windPoint.speed) || 0
    const iconIndex = getWindBarbIconIndex(speed)
    iconImage = getWindLegendIcon(iconIndex)
    
    // 检查图标是否存在
    if (!iconImage) {
      console.error('风羽图图标获取失败，图标类型:', iconType, '图标索引:', iconIndex)
      return null
    }
  }
  
  // 创建自定义风向标记（带飘动效果）
  const marker = createFloatingWindMarker(windPoint, markerSize, iconImage)
  
  // 保存 windPoint 数据到 marker 的 extData，以便在缩放时重新生成 Canvas 图标
  if (marker && marker.setExtData) {
    marker.setExtData({ 
      ...marker.getExtData(),
      windPoint: windPoint 
    })
  }

  // 延迟创建 InfoWindow，只在点击时创建，减少内存占用
  // 设置点击事件处理函数
  if (marker.onClick) {
    // 自定义覆盖物的点击事件
    marker.onClick = () => {
      // 隐藏最大风速点弹窗
      clearInfoWindows()
      
      // 点击时才创建 InfoWindow
      const infoWindow = new window.AMap.InfoWindow({
        content: `
          <div style="padding: 8px;">
            <div><strong>风向:</strong> ${windPoint.direction}°</div>
            <div><strong>风速:</strong> ${windPoint.speed} m/s</div>
            <div><strong>位置:</strong> ${windPoint.lng}, ${windPoint.lat}</div>
            <div><strong>时间:</strong> ${windPoint.time || 'N/A'}</div>
          </div>
        `,
        offset: new window.AMap.Pixel(0, -30)
      })
      infoWindow.open(map, [windPoint.lng, windPoint.lat])
    }
  } else {
    // 标准标记的点击事件
    marker.on('click', () => {
      // 隐藏最大风速点弹窗
      clearInfoWindows()
      
      // 点击时才创建 InfoWindow
      const infoWindow = new window.AMap.InfoWindow({
        content: `
          <div style="padding: 8px;">
            <div><strong>风向:</strong> ${windPoint.direction}°</div>
            <div><strong>风速:</strong> ${windPoint.speed} m/s</div>
            <div><strong>位置:</strong> ${windPoint.lng}, ${windPoint.lat}</div>
            <div><strong>时间:</strong> ${windPoint.time || 'N/A'}</div>
          </div>
        `,
        offset: new window.AMap.Pixel(0, -30)
      })
      infoWindow.open(map, marker.getPosition())
    })
  }

  return marker
}

async function addWindMarkers() {
  console.log('开始渲染风羽图...')
  const startTime = performance.now()
  
  // 获取当前缩放级别
  const currentZoom = map.getZoom()
  
  // 如果数据已加载，直接使用缓存数据
  if (isDataLoaded && cachedData) {
    console.log('使用缓存数据渲染风羽图')
    // 先清除之前的标记
    clearMapMarkers()
    clearInfoWindows()
    clearMaxMarkers()
    renderWindMarkersFromCache(cachedData, currentZoom)
    const endTime = performance.now()
    console.log(`风羽图渲染完成，耗时: ${(endTime - startTime).toFixed(2)}ms`)
    return
  }
  
  // 获取新数据
  console.log('获取新数据...')
  const windData = await getData()
  
  // 检查数据是否为空
  if (!windData || windData.length === 0) {
    // 数据为空时，隐藏渲染中提示，不清除地图上的撒点
    isCanvasRendering.value = false
    return
  }
  
  // 数据有效时，先清除之前的标记，再渲染新数据
  clearMapMarkers()
  clearInfoWindows()
  clearMaxMarkers()
  
  // 处理数据
  if (windData && windData.length > 0) {
    renderWindMarkersFromCache(windData, currentZoom)
  } else {
    isCanvasRendering.value = false
  }
}

// 更新风向标记位置（根据当前缩放级别重新计算）
async function updateWindMarkersPosition() {
  if (!map || !isDataLoaded || !cachedData) return
  
  try {
    const currentZoom = map.getZoom()
    clearMapMarkers()
    clearInfoWindows()
    renderWindMarkersFromCache(cachedData, currentZoom)
  } catch (error) {
    console.error('更新风向标记位置失败:', error)
    isCanvasRendering.value = false
  }
}

// 笛卡尔坐标转武汉经纬度坐标函数
function cartesianToWuhanGCJ02(x, y, options = {}) {
  // 武汉市中心坐标（默认原点）
  const {
    originLng = 117.050972, // 武汉市中心经度
    originLat = 32.390648, // 武汉市中心纬度
    scale = 100000, // 比例尺：1单位代表多少米（需要根据实际情况调整）
    isMeters = true // 坐标单位是否为米
  } = options

  // 添加调试信息

  // 地球半径（米）
  const earthRadius = 6378137

  // 计算经纬度偏移量
  let deltaLng, deltaLat

  if (isMeters) {
    // 如果坐标单位是米
    // 经度偏移计算（考虑纬度因素）
    deltaLng = ((x / scale) * (180 / Math.PI)) / (earthRadius * Math.cos((originLat * Math.PI) / 180))
    // 纬度偏移计算
    deltaLat = ((y / scale) * (180 / Math.PI)) / earthRadius
  } else {
    // 如果坐标单位是度
    deltaLng = x / scale
    deltaLat = y / scale
  }

  // 计算WGS84经纬度（武汉）
  const wgs84Lng = originLng + deltaLng
  const wgs84Lat = originLat + deltaLat


  // 将WGS84转换为GCJ02（高德地图坐标系）
  return wgs84ToGcj02(wgs84Lng, wgs84Lat)
}

// WGS84转GCJ02的转换函数
function wgs84ToGcj02(lng, lat) {
  const a = 6378245.0
  const ee = 0.00669342162296594323

  if (outOfChina(lng, lat)) {
    return [lng, lat]
  }

  let dLat = transformLat(lng - 105.0, lat - 35.0)
  let dLng = transformLng(lng - 105.0, lat - 35.0)

  const radLat = (lat / 180.0) * Math.PI
  let magic = Math.sin(radLat)
  magic = 1 - ee * magic * magic

  const sqrtMagic = Math.sqrt(magic)
  dLat = (dLat * 180.0) / (((a * (1 - ee)) / (magic * sqrtMagic)) * Math.PI)
  dLng = (dLng * 180.0) / ((a / sqrtMagic) * Math.cos(radLat) * Math.PI)

  return [lng + dLng, lat + dLat]
}

function outOfChina(lng, lat) {
  return lng < 72.004 || lng > 137.8347 || lat < 0.8293 || lat > 55.8271
}

function transformLat(x, y) {
  let ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x))
  ret += ((20.0 * Math.sin(6.0 * x * Math.PI) + 20.0 * Math.sin(2.0 * x * Math.PI)) * 2.0) / 3.0
  ret += ((20.0 * Math.sin(y * Math.PI) + 40.0 * Math.sin((y / 3.0) * Math.PI)) * 2.0) / 3.0
  ret += ((160.0 * Math.sin((y / 12.0) * Math.PI) + 320 * Math.sin((y * Math.PI) / 30.0)) * 2.0) / 3.0
  return ret
}

function transformLng(x, y) {
  let ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x))
  ret += ((20.0 * Math.sin(6.0 * x * Math.PI) + 20.0 * Math.sin(2.0 * x * Math.PI)) * 2.0) / 3.0
  ret += ((20.0 * Math.sin(x * Math.PI) + 40.0 * Math.sin((x / 3.0) * Math.PI)) * 2.0) / 3.0
  ret += ((150.0 * Math.sin((x / 12.0) * Math.PI) + 300.0 * Math.sin((x / 30.0) * Math.PI)) * 2.0) / 3.0
  return ret
}
// 根据地图缩放级别计算动态比例尺
function getDynamicScale(zoom) {
  // 缩放级别与比例尺的对应关系
  // 缩放级别越大，比例尺越小（更精确）
  // 使用更小的比例尺值，避免坐标被过度压缩
  const scaleMap = {
    8: 500, // 缩放级别8对应比例尺500
    9: 300, // 缩放级别9对应比例尺300
    10: 200, // 缩放级别10对应比例尺200
    11: 150, // 缩放级别11对应比例尺150
    12: 100, // 缩放级别12对应比例尺100
    13: 80, // 缩放级别13对应比例尺80
    14: 60, // 缩放级别14对应比例尺60
    15: 40, // 缩放级别15对应比例尺40
    16: 30, // 缩放级别16对应比例尺30
    17: 20, // 缩放级别17对应比例尺20
    18: 15, // 缩放级别18对应比例尺15
    19: 10 // 缩放级别19对应比例尺10
  }

  // 获取当前缩放级别
  const currentZoom = zoom || map.getZoom()

  // 根据缩放级别返回对应的比例尺，如果没有精确匹配则进行插值计算
  if (scaleMap[currentZoom]) {
    return scaleMap[currentZoom]
  }

  // 插值计算：找到最接近的两个缩放级别
  const zoomLevels = Object.keys(scaleMap)
    .map(Number)
    .sort((a, b) => a - b)
  let lowerZoom = zoomLevels[0]
  let upperZoom = zoomLevels[zoomLevels.length - 1]

  for (let i = 0; i < zoomLevels.length - 1; i++) {
    if (currentZoom >= zoomLevels[i] && currentZoom <= zoomLevels[i + 1]) {
      lowerZoom = zoomLevels[i]
      upperZoom = zoomLevels[i + 1]
      break
    }
  }

  // 线性插值计算比例尺
  const lowerScale = scaleMap[lowerZoom]
  const upperScale = scaleMap[upperZoom]
  const ratio = (currentZoom - lowerZoom) / (upperZoom - lowerZoom)

  return Math.round(lowerScale + (upperScale - lowerScale) * ratio)
}

// 处理缓存数据的函数
function processCachedData() {
  if (!cachedData || !Array.isArray(cachedData)) {
    return []
  }

  // 获取当前地图的缩放级别
  const currentZoom = map ? map.getZoom() : 12
  const dynamicScale = getDynamicScale(currentZoom)

  // 创建数据副本并处理坐标转换
  const processedData = cachedData.map((r) => {
    return {
      ...r,
      lng: r.xCoordinate * 1,
      lat: r.yCoordinate * 1,
      direction: r.windDirection,
      speed: r.windSpeed
    }
  })

  return processedData
}

// 清除缓存数据的函数（用于重新加载数据）
function clearCache() {
  cachedData = null
  isDataLoaded = false
}

// 清除地图上所有撒点和覆盖物
function clearAllMapOverlays() {
  // 清除风羽图标记
  clearMapMarkers()
  
  // 清除热力图
  clearHeatmap()
  
  // 清除粒子系统
  clearParticleSystem()
  
  // 清除雷达扫描覆盖层
  removeRadarOverlay()
  
  // 清除最大值标记
  clearMaxMarkers()
  
  // 清除信息窗体
  clearInfoWindows()
  
  // 清除缓存数据
  clearCache()
  
  console.log('已清除地图上所有撒点和覆盖物')
}

// 清除地图上的所有标记
function clearMapMarkers() {
  // 取消任何在进行中的风羽图批量渲染
  if (windBatchAnimId) {
    cancelAnimationFrame(windBatchAnimId)
    windBatchAnimId = null
  }
  
  // 移除统一的缩放事件监听器
  if (globalZoomHandler && map) {
    map.off('zoomchange', globalZoomHandler)
    map.off('zoomend', globalZoomHandler)
    globalZoomHandler = null
  }
  
  // 清除缩放更新相关的定时器和动画
  if (zoomUpdateTimer) {
    clearTimeout(zoomUpdateTimer)
    zoomUpdateTimer = null
  }
  if (zoomUpdateAnimId) {
    cancelAnimationFrame(zoomUpdateAnimId)
    zoomUpdateAnimId = null
  }
  
  // 重置缩放状态
  currentZoomLevel = null
  isZoomUpdating = false
  
  markers.forEach((marker) => {
    if (marker && map) {
      map.remove(marker)
    }
  })
  markers = []

  if (windMassMarks) {
    try {
      windMassMarks.setMap(null)
      windMassMarks.clear()
    } catch (error) {
      console.warn('清除 MassMarks 失败:', error)
    }
    windMassMarks = null
  }
  
  // 清理 Icon 缓存（可选，如果内存紧张）
  // iconCache.clear()
}

// 处理最大风速旗子显示
function handleShowMaxSpeedFlag(data) {
  console.log('handleShowMaxSpeedFlag called with data:', data)
  console.log('flag image path:', flag)
  
  if (!map) {
    console.log('Map not available')
    return
  }
  
  // 移除现有的旗子标记
  if (maxSpeedFlagMarker) {
    map.remove(maxSpeedFlagMarker)
    maxSpeedFlagMarker = null
  }
  
  // 如果需要显示旗子
  if (data.show && data.position) {
    console.log('Creating flag marker at position:', data.position)
    
    // 创建旗子标记（使用DOM内容，确保资源加载与层级显示）
    try {
      const flagEl = document.createElement('div')
      flagEl.style.width = '32px'
      flagEl.style.height = '32px'
      flagEl.style.backgroundImage = `url(${flag})`
      flagEl.style.backgroundRepeat = 'no-repeat'
      flagEl.style.backgroundPosition = 'center bottom'
      flagEl.style.backgroundSize = 'contain'
      flagEl.style.pointerEvents = 'auto'
      
      maxSpeedFlagMarker = new window.AMap.Marker({
        position: [data.position.lng, data.position.lat],
        title: `最大风速: ${data.speed} m/s`,
        content: flagEl,
        offset: new window.AMap.Pixel(0, -16),
        zIndex: 2000
      })
      console.log('Flag DOM marker created successfully')
    } catch (error) {
      console.error('Error creating DOM flag marker:', error)
    }
    
    // 添加到地图
    map.add(maxSpeedFlagMarker)
    console.log('Flag marker added to map:', maxSpeedFlagMarker)
    
    // 点击旗子显示信息
    maxSpeedFlagMarker.on('click', () => {
      const infoWindow = new window.AMap.InfoWindow({
        content: `
          <div style="padding: 10px; background: rgba(30,75,36,0.6); color: white; border-radius: 6px;">
            <h4 style="margin: 0 0 8px 0; color: #3b82f6;">🚩 最大风速点</h4>
            <p style="margin: 4px 0;"><strong>风速:</strong> ${data.speed} m/s</p>
            <p style="margin: 4px 0;"><strong>位置:</strong> ${data.position.lng.toFixed(6)}, ${data.position.lat.toFixed(6)}</p>
          </div>
        `,
        offset: new window.AMap.Pixel(0, -30)
      })
      infoWindow.open(map, [data.position.lng, data.position.lat])
    })
  } else {
    console.log('Not showing flag - show:', data.show, 'position:', data.position)
  }
}

// 处理最大风向图标显示
function handleShowMaxDirectionFlag(data) {
  console.log('handleShowMaxDirectionFlag called with data:', data)
  console.log('direction image path:', directionIcon)
  
  if (!map) {
    console.log('Map not available')
    return
  }
  
  // 移除现有的最大风向标记
  if (maxDirectionFlagMarker) {
    map.remove(maxDirectionFlagMarker)
    maxDirectionFlagMarker = null
  }
  
  // 如果需要显示图标
  if (data.show && data.position) {
    try {
      const dirEl = document.createElement('div')
      dirEl.style.width = '32px'
      dirEl.style.height = '32px'
      dirEl.style.backgroundImage = `url(${directionIcon})`
      dirEl.style.backgroundRepeat = 'no-repeat'
      dirEl.style.backgroundPosition = 'center bottom'
      dirEl.style.backgroundSize = 'contain'
      dirEl.style.pointerEvents = 'auto'
      
      maxDirectionFlagMarker = new window.AMap.Marker({
        position: [data.position.lng, data.position.lat],
        title: `最大风向: ${data.direction}°`,
        content: dirEl,
        offset: new window.AMap.Pixel(0, -16),
        zIndex: 2000
      })
    } catch (error) {
      console.error('Error creating DOM direction marker:', error)
    }
    
    // 添加到地图
    map.add(maxDirectionFlagMarker)
    
    // 点击显示信息窗体
    maxDirectionFlagMarker.on('click', () => {
      const infoWindow = new window.AMap.InfoWindow({
        content: `
          <div style="padding: 10px; background: rgba(30,75,36,0.6); color: white; border-radius: 6px;">
            <h4 style="margin: 0 0 8px 0; color: #10b981;">🧭 最大风向点</h4>
            <p style="margin: 4px 0;"><strong>风向:</strong> ${data.direction}°</p>
            <p style="margin: 4px 0;"><strong>位置:</strong> ${data.position.lng.toFixed(6)}, ${data.position.lat.toFixed(6)}</p>
          </div>
        `,
        offset: new window.AMap.Pixel(0, -30)
      })
      infoWindow.open(map, [data.position.lng, data.position.lat])
    })
  }
}

// 清除信息窗体
function clearInfoWindows() {
  if (maxSpeedInfoWindow) {
    maxSpeedInfoWindow.close()
  }
  if (minSpeedInfoWindow) {
    minSpeedInfoWindow.close()
  }
  
  // 关闭所有marker的信息窗体
  if (map) {
    map.clearInfoWindow()
  }
  
  // 清除自定义覆盖物
  customOverlays.forEach(overlay => {
    if (overlay) {
      if (overlay.remove) {
        // 新的简单格式
        overlay.remove()
      } else if (overlay.setMap) {
        // 标准AMap覆盖物格式
        overlay.setMap(null)
      } else if (map) {
        // 尝试从地图移除
        map.remove(overlay)
      }
    }
  })
  customOverlays = []
}

// 创建自定义信息窗体覆盖物
function createCustomInfoOverlay(position, content, className) {
  // 创建DOM元素
  const div = document.createElement('div')
  div.className = className
  div.innerHTML = content
  div.style.position = 'absolute'
  div.style.zIndex = '2000' // 提高z-index，确保显示在图标上方
  div.style.pointerEvents = 'auto'
  div.style.backgroundColor = 'white'
  div.style.borderRadius = '8px'
  div.style.boxShadow = '0 4px 8px rgba(0,0,0,0.2)'
  
  // 检查 AMap.Overlay 是否可用
  if (!window.AMap || !window.AMap.Overlay) {
    // 使用简单的DOM元素方法
    const mapContainer = map.getContainer()
    
    if (!mapContainer) {
      console.error('地图容器不存在，无法添加DOM元素')
      return null
    }
    
    mapContainer.appendChild(div)
    
    // 手动计算位置
    const updatePosition = () => {
      try {
        const pixel = map.lngLatToContainer(position)
        // 调整位置，让信息窗体显示在图标上方合适的位置
        div.style.left = (pixel.x - 125) + 'px' // 向左偏移125px，让弹窗居中
        div.style.top = (pixel.y - 200) + 'px' // 向上偏移200px，确保不遮挡图标
      } catch (error) {
        console.error('更新位置失败:', error)
      }
    }
    
    // 初始位置
    updatePosition()
    
    // 监听地图事件更新位置
    const moveHandler = () => updatePosition()
    const zoomHandler = () => updatePosition()
    
    map.on('moveend', moveHandler)
    map.on('zoomend', zoomHandler)
    
    // 添加一些额外的样式确保可见性
    div.style.display = 'block'
    div.style.visibility = 'visible'
    div.style.opacity = '1'
    
    // 返回一个简单的对象
    return {
      element: div,
      position: position,
      remove: () => {
        if (div && div.parentNode) {
          div.parentNode.removeChild(div)
        }
        map.off('moveend', moveHandler)
        map.off('zoomend', zoomHandler)
      }
    }
  }
  
  // 如果 AMap.Overlay 可用，使用标准方法
  try {
    class CustomInfoOverlay extends window.AMap.Overlay {
      constructor(position, element) {
        super()
        this.position = position
        this.element = element
      }
      
      // 初始化覆盖物
      onAdd() {
        this.getMap().getContainer().appendChild(this.element)
      }
      
      // 移除覆盖物
      onRemove() {
        if (this.element && this.element.parentNode) {
          this.element.parentNode.removeChild(this.element)
        }
      }
      
      // 绘制覆盖物
      draw() {
        const pixel = this.getMap().lngLatToContainer(this.position)
        this.element.style.left = (pixel.x - 125) + 'px' // 向左偏移125px，让弹窗居中
        this.element.style.top = (pixel.y - 200) + 'px' // 向上偏移200px，确保不遮挡图标
      }
    }
    
    const overlay = new CustomInfoOverlay(position, div)
    overlay.setMap(map)
    
    return overlay
  } catch (error) {
    console.error('创建自定义覆盖物失败:', error)
    // 回退到简单方法
    const mapContainer = map.getContainer()
    mapContainer.appendChild(div)
    
    const pixel = map.lngLatToContainer(position)
    div.style.left = (pixel.x - 125) + 'px' // 向左偏移125px，让弹窗居中
    div.style.top = (pixel.y - 200) + 'px' // 向上偏移200px，确保不遮挡图标
    
    return {
      element: div,
      position: position,
      remove: () => {
        if (div && div.parentNode) {
          div.parentNode.removeChild(div)
        }
      }
    }
  }
}

// 计算并显示风速统计信息
function showSpeedStatistics(windData) {
  if (!windData || windData.length === 0) {
    return
  }
  
  // 过滤有效风速数据
  const validWindData = windData.filter(point => {
    const speed = parseFloat(point.speed)
    return !isNaN(speed) && speed !== -9999
  })
  
  if (validWindData.length === 0) {
    return
  }
  
  // 找到最大风速和最小风速的数据点
  const maxSpeedPoint = validWindData.reduce((max, point) => 
    parseFloat(point.speed) > parseFloat(max.speed) ? point : max
  )
  const minSpeedPoint = validWindData.reduce((min, point) => 
    parseFloat(point.speed) < parseFloat(min.speed) ? point : min
  )
  
  // 关闭之前的信息窗体
  if (maxSpeedInfoWindow) {
    maxSpeedInfoWindow.close()
  }
  if (minSpeedInfoWindow) {
    minSpeedInfoWindow.close()
  }
  
  // 清除之前的自定义覆盖物
  clearInfoWindows()
  
  // 检查是否是同一个点
  const isSamePoint = maxSpeedPoint.lng === minSpeedPoint.lng && maxSpeedPoint.lat === minSpeedPoint.lat
}
async function addArticle() { 
  // 注意：switchMapType 已经清除了所有标记，这里不需要重复清除
  // 但是为了确保其他模式也能正常工作，仍然清除热力图和粒子系统
  clearHeatmap()
  clearParticleSystem()
  
  // 根据当前地图类型决定显示内容
  if (mapType.value === 'satellite') {
    // 风羽图模式：显示风向标（addWindMarkers 内部会清除标记）
    await addWindMarkers()
  } else if (mapType.value === 'heatmap') {
    // 热力图模式：显示热力图
    await addHeatmap()
  } else if (mapType.value === 'radar') {
    // 雷达回波模式：显示雷达回波热力图
    await addRadarEcho()
  } else if (mapType.value === 'particle') {
    // 粒子图模式：显示粒子系统
    await addParticleSystem()
  }
}
async function getData() {
 
  try {
    
    const res = await windDataPPI(query.value)
    let data = res.data.result
    // 检查数据是否为空
    if (!data || !Array.isArray(data) || data.length === 0) {
      // 显示提示框，维持7秒
      ElMessage({
        message: '暂无有效数据',
        type: 'warning',
        duration: 10000
      })
      // 返回空数组，停止撒点，不处理实时数据
      return []
    }
    
    // 数据有效时，才处理实时数据（实时风速和实时风向）
    realTime(res.data.result)  
    if (data && Array.isArray(data)) {
      // 为每个数据点添加随机时间
      let baseTime = new Date(query.value.timeParam)
      
      // 如果基础时间无效，使用当前时间
      if (isNaN(baseTime.getTime())) {
        console.warn('timeParam无效，使用当前时间作为基础时间')
        baseTime = new Date()
      }
      
      data = data.map((item, index) => {
        // 生成随机秒数（0-300秒，即5分钟内）
        const randomSeconds = Math.floor(Math.random() * 300)
        const randomTime = new Date(baseTime.getTime() + randomSeconds * 1000)
        
        // 格式化时间为 YYYY-MM-DD HH:mm:ss
        const dateString = randomTime.toLocaleDateString('zh-CN', {
          year: 'numeric',
          month: '2-digit',
          day: '2-digit'
        })
        const timeString = randomTime.toLocaleTimeString('zh-CN', {
          hour12: false,
          hour: '2-digit',
          minute: '2-digit',
          second: '2-digit'
        })
        const fullDateTimeString = `${dateString} ${timeString}`
        
        return {
          ...item,
          randomTime: fullDateTimeString,
          randomTimeFull: randomTime.toISOString()
        }
      })
      
      // 缓存原始数据
      cachedData = [...data]
      isDataLoaded = true
       // 获取当前地图的缩放级别
      const currentZoom = map ? map.getZoom() : 12
      const dynamicScale = getDynamicScale(currentZoom)
      data.forEach((r) => {
        // 尝试两种坐标转换方法
        r.lng = r.xCoordinate*1
        r.lat = r.yCoordinate * 1
        r.direction = r.windDirection
        r.speed=r.windSpeed
        // // 方法1：使用复杂的坐标转换（假设是笛卡尔坐标）
        // try {
        //   ;[lng, lat] = cartesianToWuhanGCJ02(r.xCoordinate, r.yCoordinate, { scale: dynamicScale/1000 })
        // } catch (error) {
        //   // 方法2：简单转换（假设原始数据已经是经纬度）
        //   lng = r.xCoordinate
        //   lat = r.yCoordinate
        // }

        // r.lng = lng
        // r.lat = lat

        // // 处理风向数据：转换为数字类型
        // r.direction = r.direction * 1

        // // 处理风速数据：转换为数字类型
        // r.speed = r.speed * 1
      })
      return data
    } else {
      return []
    }
  } catch (error) {
    throw error
  }
}
async function gerPpiDate() { 
  // 确保 city 参数包含在查询参数中
  const response = await tableQuery({
    ...queryParams.value,
    city: queryParams.value.city || 'hefei', // 确保 city 参数存在
  })
  // const response1 = await getTableList({
  //   ...queryParams.value,
  //   city: queryParams.value.city || 'hefei', // 确保 city 参数存在
  // })
  
  // console.log(response1,"获取表结构");
    if (response.code === 200) {
      timeOptions.value = response.data.result || []
      // 如果有数据且当前没有选择时间，自动选择第一个
      if (timeOptions.value.length > 0) {
        const latestTime = timeOptions.value[0].dateTimeString
        query.value.timeParam = latestTime
        // 每次定时刷新前清除缓存，确保重新请求windDataPPI刷新撒点
        clearCache()
        // 自动选择第一个时间后，获取对应的数据
        await addArticle()
      }
    }
    
    // 检查时间差，判断是否断线
    checkConnectionStatus()
}

// 检查连接状态的方法
function checkConnectionStatus() {
  if (!query.value.timeParam) {
    return
  }
  
  const currentTime = new Date()
  // 解析格式为 YYYYMMDDHHmmss 的时间字符串
  const timeStr = query.value.timeParam
  const dataTime = new Date(
    timeStr.replace(/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/, '$1-$2-$3 $4:$5:$6')
  )
  
  // 计算时间差（分钟）
  const timeDiffMinutes = (currentTime.getTime() - dataTime.getTime()) / (1000 * 60)
  
  // 如果时间差超过20分钟，则判断为断线
  const isDisconnected = timeDiffMinutes > 25
  
  // 触发断线状态更新事件
  window.dispatchEvent(new CustomEvent('connectionStatusChanged', {
    detail: { isDisconnected }
  }))
}
// 风羽图扫描线相关变量
let satelliteScanCanvas = null
let satelliteScanCtx = null
let satelliteScanAnimId = null
let satelliteScanAngle = 0
// 卫星中心点坐标（由雷达选择动态控制）
const centerLng = ref(117.050972)
const centerLat = ref(32.390648)
let satelliteCenter = [117.050972, 32.390648]
let centerMarker = null

// 初始化风羽图扫描线
function initSatelliteScan() {
  if (!map) return
  
  // 根据动态中心更新扫描中心
  satelliteCenter = [centerLng.value, centerLat.value]
  
  // 创建扫描线Canvas
  const container = map.getContainer()
  satelliteScanCanvas = document.createElement('canvas')
  satelliteScanCanvas.className = 'satellite-scan-overlay'
  satelliteScanCanvas.style.position = 'absolute'
  satelliteScanCanvas.style.top = '0'
  satelliteScanCanvas.style.left = '0'
  satelliteScanCanvas.style.pointerEvents = 'none'
  satelliteScanCanvas.style.zIndex = '998'
  satelliteScanCanvas.style.opacity = '0.8'
  
  container.appendChild(satelliteScanCanvas)
  satelliteScanCtx = satelliteScanCanvas.getContext('2d')
  
  // 开始扫描动画
  startSatelliteScan()
}

// 开始风羽图扫描动画
function startSatelliteScan() {
  if (!satelliteScanCtx || !map) return
  
  function drawSatelliteScan() {
    if (!satelliteScanCtx || !map) return
    
    // 获取容器尺寸
    const container = map.getContainer()
    satelliteScanCanvas.width = container.clientWidth
    satelliteScanCanvas.height = container.clientHeight
    
    // 获取卫星中心点在地图容器中的像素坐标
    const center = map.lngLatToContainer(satelliteCenter)
    
    // 清除画布
    satelliteScanCtx.clearRect(0, 0, satelliteScanCanvas.width, satelliteScanCanvas.height)
    
    // 计算扫描半径（根据地图缩放级别动态调整）
    const currentZoom = map.getZoom()
    const baseRadius = 300
    const zoomScale = Math.pow(2, currentZoom - 12) // 以12级为基准
    const radius = baseRadius * zoomScale
    
    // 绘制扫描线
    const endX = center.x + radius * Math.cos(satelliteScanAngle)
    const endY = center.y + radius * Math.sin(satelliteScanAngle)
    
    // 绘制扫描线主体
    satelliteScanCtx.strokeStyle = 'rgba(0, 255, 0, 0.8)'
    satelliteScanCtx.lineWidth = 3
    satelliteScanCtx.beginPath()
    satelliteScanCtx.moveTo(center.x, center.y)
    satelliteScanCtx.lineTo(endX, endY)
    satelliteScanCtx.stroke()
    
    // 绘制扫描线端点
    satelliteScanCtx.fillStyle = 'rgba(0, 255, 0, 0.9)'
    satelliteScanCtx.beginPath()
    satelliteScanCtx.arc(endX, endY, 4, 0, Math.PI * 2)
    satelliteScanCtx.fill()
    
    // 绘制扫描扇形（扫描线后面的扇形区域）
    satelliteScanCtx.fillStyle = 'rgba(0, 255, 0, 0.1)'
    satelliteScanCtx.beginPath()
    satelliteScanCtx.moveTo(center.x, center.y)
    satelliteScanCtx.arc(center.x, center.y, radius, satelliteScanAngle - 0.15, satelliteScanAngle + 0.15)
    satelliteScanCtx.closePath()
    satelliteScanCtx.fill()
    
    // 绘制扫描扇形边框
    satelliteScanCtx.strokeStyle = 'rgba(0, 255, 0, 0.3)'
    satelliteScanCtx.lineWidth = 1
    satelliteScanCtx.beginPath()
    satelliteScanCtx.moveTo(center.x, center.y)
    satelliteScanCtx.arc(center.x, center.y, radius, satelliteScanAngle - 0.15, satelliteScanAngle + 0.15)
    satelliteScanCtx.stroke()
    
    // 更新扫描角度（使用可配置的速度值）
    satelliteScanAngle += scanSpeed.value
    if (satelliteScanAngle > Math.PI * 2) {
      satelliteScanAngle = 0
    }
    
    // 继续动画
    satelliteScanAnimId = requestAnimationFrame(drawSatelliteScan)
  }
  
  // 开始动画
  drawSatelliteScan()
}

// 从缓存数据渲染风羽图（MassMarks版本）
function renderWindMarkersFromCache(cached, currentZoom) {
  if (!map || !cached || cached.length === 0) {
    isCanvasRendering.value = false
    return
  }

  if (mapType && mapType.value !== 'satellite') {
    isCanvasRendering.value = false
    return
  }

  const processedPoints = normalizeWindPoints(cached)
  if (processedPoints.length === 0) {
    isCanvasRendering.value = false
    return
  }

  const validWindData = processedPoints.filter(point => {
    const speed = parseFloat(point.speed)
    return !isNaN(speed) && speed !== -9999
  })

  if (validWindData.length === 0) {
    isCanvasRendering.value = false
    return
  }

  let maxSpeedPoint = validWindData[0]
  let minSpeedPoint = validWindData[0]

  validWindData.forEach((point) => {
    if (parseFloat(point.speed) > parseFloat(maxSpeedPoint.speed)) {
      maxSpeedPoint = point
    }
    if (parseFloat(point.speed) < parseFloat(minSpeedPoint.speed)) {
      minSpeedPoint = point
    }
  })

  const specialMarkers = []
  processedPoints.forEach((point) => {
    if (
      Math.abs(point.lng - maxSpeedPoint.lng) < 0.0001 &&
      Math.abs(point.lat - maxSpeedPoint.lat) < 0.0001
    ) {
      point.isMaxSpeedPoint = true
      try {
        const marker = createWindMarker(point, currentZoom, 'max')
        if (marker) {
          specialMarkers.push(marker)
        }
      } catch (error) {
        console.error('创建最大风速标记失败:', error)
      }
    } else if (
      Math.abs(point.lng - minSpeedPoint.lng) < 0.0001 &&
      Math.abs(point.lat - minSpeedPoint.lat) < 0.0001
    ) {
      point.isMinSpeedPoint = true
    }
  })

  if (specialMarkers.length > 0) {
    markers.push(...specialMarkers)
    map.add(specialMarkers.length === 1 ? specialMarkers[0] : specialMarkers)
  }

  const normalPoints = processedPoints.filter(
    point => !point.isMaxSpeedPoint && !point.isMinSpeedPoint
  )

  if (normalPoints.length > 0) {
    const { massData, styles, styleKeys, styleMetaMap } = buildMassMarksPayload(normalPoints, currentZoom)
    if (massData.length > 0) {
      if (windMassMarks) {
        try {
          windMassMarks.setMap(null)
          windMassMarks.clear()
        } catch {}
      }
      windMassMarks = new window.AMap.MassMarks(massData, {
        opacity: 1,
        zIndex: 120,
        cursor: 'pointer',
        style: styles
      })
      windMassMarks.setMap(map)
      windMassMarks.on('click', handleMassMarkerClick)
    }
  }

  showSpeedStatistics(processedPoints)
  isCanvasRendering.value = false
}

// 清除风羽图扫描线
function clearSatelliteScan() {
  console.log('清除扫描线：开始清理')
  
  // 停止动画
  if (satelliteScanAnimId) {
    cancelAnimationFrame(satelliteScanAnimId)
    satelliteScanAnimId = null
    console.log('清除扫描线：动画已停止')
  }
  
  // 清除画布内容
  if (satelliteScanCtx && satelliteScanCanvas) {
    satelliteScanCtx.clearRect(0, 0, satelliteScanCanvas.width, satelliteScanCanvas.height)
    console.log('清除扫描线：画布内容已清除')
  }
  
  // 移除DOM元素
  if (satelliteScanCanvas && satelliteScanCanvas.parentNode) {
    satelliteScanCanvas.parentNode.removeChild(satelliteScanCanvas)
    console.log('清除扫描线：DOM元素已移除')
  }
  
  // 重置变量
  satelliteScanCanvas = null
  satelliteScanCtx = null
  satelliteScanAngle = 0 // 重置扫描角度
  
  console.log('清除扫描线：清理完成')
}

onMounted(async () => {
  try {
    // 初始化北京时间显示
    updateBeijingTime()
    beijingTimeTimer = setInterval(updateBeijingTime, 1000) // 每秒更新一次
    
    await loadAMap()
    initMap()
    gerPpiDate()
    if (ppiTimer) {
      clearInterval(ppiTimer)
      ppiTimer = null
    }
    // 设置PPI数据定时器，每3分钟刷新一次
    ppiTimer = setInterval(() => {
      gerPpiDate()
    }, 1000*60*9) // 180000毫秒 = 3分钟
    
    // 初始化 Canvas 图例（默认使用 Canvas 模式）
    if (windLegendMode.value === 'canvas') {
      nextTick(() => {
        drawLegendCanvases()
      })
    }
  } catch (e) {
    console.error('初始化失败:', e)
  }
})

// 监听来自设备状态面板的雷达选择变更
window.addEventListener('radar-selected', async (e) => {
  try {
    const { lat, lng, city } = e.detail || {}
    // 更新地图中心位置
    if (typeof lat === 'number' && typeof lng === 'number' && !Number.isNaN(lat) && !Number.isNaN(lng)) {
      centerLat.value = lat
      centerLng.value = lng
      satelliteCenter = [centerLng.value, centerLat.value]
      if (map) {
        map.setCenter([centerLng.value, centerLat.value])
      }
      if (centerMarker) {
        centerMarker.setPosition([centerLng.value, centerLat.value])
      }
    }
    // 更新城市参数（合肥站 -> hefei, 宣城站 -> xuancheng）
    if (city && (city === 'hefei' || city === 'xuancheng')) {
      const oldCity = queryParams.value.city
      queryParams.value.city = city
      query.value.city = city // Update query for data fetching
      console.log('雷达选择变更，城市参数已更新为:', city)
      
      if (oldCity !== city) {
        // 如果城市参数发生变化，重新加载数据
        queryParams.value.pageNum = 1
        query.value.pageNum = 1
        
        // 清除缓存数据，强制重新获取数据
        clearCache()
        
        // 清除地图上的所有撒点和覆盖物
        clearAllMapOverlays()
        
        // 清除之前的选择状态，确保重新加载后默认选中第一个
        if (mapType.value === 'satellite' || mapType.value === 'heatmap') {
          // 风羽图和热力图模式：清除时间选择
          query.value.timeParam = ''
        }
        
        // 重新加载数据并渲染
        await gerPpiDate()
        
        // 根据当前模式调用对应的渲染函数
        if (mapType.value === 'satellite') {
          // 风羽图模式：重新渲染撒点
          await addWindMarkers()
        } else if (mapType.value === 'heatmap') {
          // 热力图模式：重新渲染热力图
          await addHeatmap()
        }
        
        console.log('城市参数变化，已清除缓存和地图撒点，数据列表和地图渲染已重新加载')
      }
    }
  } catch (error) {
    console.error('处理雷达选择变更失败:', error)
  }
})

onBeforeUnmount(() => {
  // 清除北京时间定时器
  if (beijingTimeTimer) {
    clearInterval(beijingTimeTimer)
    beijingTimeTimer = null
  }
  
  try {
    markers.forEach((marker) => {
      if (marker) map.remove(marker)
    })
  } catch {}
  try {
    clearInfoWindows()
  } catch {}
  try {
    clearHeatmap()
  } catch {}
  try {
    clearParticleSystem()
  } catch {}
  try {
    clearSatelliteScan()
  } catch {}
  try {
    if (map) map.destroy()
  } catch {}
  
  markers = []
  map = null
  maxSpeedInfoWindow = null
  minSpeedInfoWindow = null
  customOverlays = []
  particleSystem = null
})
</script>

<style scoped lang="scss">

.heatmap-page {
  width: 100%;
  height: 100%;
  position: relative;
  
  // 数据加载遮罩层
  .data-loading-overlay {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 9999;
    background: rgba(0, 0, 0, 0.6);
    backdrop-filter: blur(4px);
    display: flex;
    align-items: center;
    justify-content: center;
    pointer-events: all;
    
    .loading-content {
      display: flex;
      flex-direction: column;
      align-items: center;
      gap: 20px;
      padding: 30px 50px;
      background: linear-gradient(135deg, rgba(24, 150, 146, 0.95) 0%, rgba(24, 150, 146, 0.85) 100%);
      border-radius: 12px;
      box-shadow: 
        0 8px 24px rgba(0, 0, 0, 0.3),
        0 4px 8px rgba(0, 0, 0, 0.2);
      border: 1px solid rgba(255, 255, 255, 0.2);
      
      .loading-spinner {
        width: 50px;
        height: 50px;
        border: 4px solid rgba(255, 255, 255, 0.3);
        border-top-color: #ffffff;
        border-radius: 50%;
        animation: spin 1s linear infinite;
      }
      
      .loading-text {
        color: #ffffff;
        font-size: 16px;
        font-weight: 500;
        letter-spacing: 1px;
      }
    }
  }
  
  .map {
    width: 100%;
    height: calc(100vh - 140px);
    min-height: 500px;
  }
  
  // 地图类型切换器
  .map-type-switcher {
    position: absolute;
    top: 80px;
    left: 380px;
    z-index: 1001;
    
    .switcher-tabs {
      display: flex;
      background: rgba(24, 150, 146, 0.12);
      border-radius: 8px;
      padding: 2px;
      box-shadow: 
        0 3px 10px rgba(24, 150, 146, 0.15),
        0 1px 3px rgba(24, 150, 146, 0.08);
      backdrop-filter: blur(10px);
      border: 1px solid rgba(24, 150, 146, 0.25);
      
      .tab-item {
        display: flex;
        align-items: center;
        padding: 4px 8px;
        border-radius: 6px;
        cursor: pointer;
        transition: all 0.3s ease;
        position: relative;
        min-width: 60px;
        justify-content: center;
        
        &:hover {
          background: rgba(24, 150, 146, 0.18);
          transform: translateY(-1px);
        }
        
        &.active {
          background: linear-gradient(135deg, rgba(24, 150, 146, 0.7), rgba(24, 150, 146, 0.5));
          color: white;
          box-shadow: 
            0 4px 8px rgba(24, 150, 146, 0.35),
            inset 0 1px 0 rgba(255, 255, 255, 0.2);
          
          .tab-icon {
            filter: brightness(0) invert(1);
          }
          
          .tab-text {
            color: white;
            font-weight: 600;
          }
        }
        
        .tab-icon {
          font-size: 12px;
          margin-right: 4px;
          transition: all 0.3s ease;
        }
        
        .tab-text {
          font-size: 11px;
          font-weight: 500;
          color: #ffffff;
          transition: all 0.3s ease;
          white-space: nowrap;
        }
      }
    }
  }
  
  // 地图控制按钮组（复位按钮 + 扫描线速度设置）
  .map-controls-group {
    position: absolute;
    bottom: 10px;
    left: 380px;
    z-index: 1001;
    display: flex;
    align-items: flex-end;
    gap: 10px;
    
    // 地图复位按钮
    .map-reset-button {
      .reset-btn {
        display: flex;
        align-items: center;
        padding: 8px 12px;
        background: rgba(24, 150, 146, 0.12);
        border-radius: 8px;
        cursor: pointer;
        transition: all 0.3s ease;
        box-shadow: 
          0 3px 10px rgba(24, 150, 146, 0.15),
          0 1px 3px rgba(24, 150, 146, 0.08);
        backdrop-filter: blur(10px);
        border: 1px solid rgba(24, 150, 146, 0.25);
        
        &:hover {
          background: rgba(24, 150, 146, 0.2);
          transform: translateY(-2px);
          box-shadow: 
            0 6px 20px rgba(24, 150, 146, 0.25),
            0 2px 6px rgba(24, 150, 146, 0.15);
        }
        
        &:active {
          transform: translateY(0);
          box-shadow: 
            0 2px 8px rgba(24, 150, 146, 0.2),
            0 1px 3px rgba(24, 150, 146, 0.1);
        }
        
        .reset-icon {
          font-size: 14px;
          margin-right: 6px;
          transition: all 0.3s ease;
        }
        
        .reset-text {
          font-size: 12px;
          font-weight: 500;
          color: #ffffff;
          transition: all 0.3s ease;
          white-space: nowrap;
        }
        
        &:hover .reset-icon {
          transform: scale(1.1);
        }
        
        &:hover .reset-text {
          color: #ffffff;
          font-weight: 600;
        }
      }
    }
    
    // 扫描线速度设置样式（在控制按钮组内）
    .scan-speed-config {
      background: linear-gradient(135deg, rgba(24, 150, 146, 0.15) 0%, rgba(24, 150, 146, 0.1) 100%);
      border: 1px solid rgba(24, 150, 146, 0.3);
      border-radius: 6px;
      padding: 10px 12px;
      box-shadow: 
        0 6px 15px rgba(24, 150, 146, 0.15),
        0 2px 4px rgba(24, 150, 146, 0.08),
        inset 0 1px 0 rgba(255, 255, 255, 0.2);
      min-width: 100px;
      backdrop-filter: blur(8px);
      transition: all 0.3s ease;
      
      .speed-config-title {
        font-size: 12px;
        font-weight: 600;
        color: #ffffff;
        margin-bottom: 0;
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 8px;
        
        .speed-config-btn {
          background: transparent;
          color: #ffffff;
          border: none;
          padding: 0;
          cursor: pointer;
          font-size: 14px;
          line-height: 1;
          opacity: 0.7;
          transition: opacity .2s ease;
          
          &:hover {
            opacity: 1;
          }
        }
      }
      
      .speed-config-editor {
        margin-top: 8px;
        padding: 8px;
        background: rgba(24, 150, 146, 0.1);
        border: 1px dashed rgba(24, 150, 146, 0.3);
        border-radius: 8px;
        
        .speed-config-row {
          display: flex;
          align-items: center;
          gap: 8px;
          flex-wrap: wrap;
          
          .speed-config-label {
            color: #ffffff;
            font-size: 12px;
            white-space: nowrap;
          }
          
          .speed-config-input {
            width: 100px;
            padding: 4px 8px;
            border-radius: 4px;
            border: 1px solid rgba(24, 150, 146, 0.4);
            background: rgba(24, 150, 146, 0.1);
            color: #ffffff;
            font-size: 12px;
            
            &:focus {
              outline: none;
              border-color: rgba(24, 150, 146, 0.6);
              background: rgba(24, 150, 146, 0.2);
            }
            
            &::placeholder {
              color: rgba(255, 255, 255, 0.5);
            }
          }
          
          .speed-config-save-btn {
            background: rgba(24, 150, 146, 0.8);
            color: #ffffff;
            border: 1px solid rgba(24, 150, 146, 0.6);
            border-radius: 4px;
            padding: 4px 12px;
            cursor: pointer;
            font-size: 12px;
            transition: all 0.2s ease;
            
            &:hover {
              background: rgba(24, 150, 146, 1);
              border-color: rgba(255, 255, 255, 0.8);
            }
          }
        }
        
        .speed-config-tip {
          color: rgba(255, 255, 255, 0.8);
          margin-top: 6px;
          font-size: 11px;
          line-height: 1.4;
        }
      }
    }
  }
  
  // 数据状态切换器
  .data-status-switcher {
    position: absolute;
    top: 20px;
    left: 380px;
    z-index: 1001;
    
    .status-tabs {
      display: flex;
      background: rgba(24, 150, 146, 0.15);
      border-radius: 8px;
      padding: 2px;
      box-shadow: 
        0 4px 12px rgba(24, 150, 146, 0.2),
        0 2px 4px rgba(24, 150, 146, 0.1);
      backdrop-filter: blur(10px);
      border: 1px solid rgba(24, 150, 146, 0.3);
      
      .status-tab-item {
        display: flex;
        align-items: center;
        padding: 6px 12px;
        border-radius: 6px;
        cursor: pointer;
        transition: all 0.3s ease;
        position: relative;
        min-width: 80px;
        justify-content: center;
        
        &:hover {
          background: rgba(24, 150, 146, 0.2);
          transform: translateY(-1px);
        }
        
        &.active {
          background: linear-gradient(135deg, rgba(255, 255, 255, 0.8), rgba(24, 150, 146, 0.6));
          color: white;
          box-shadow: 
            0 4px 8px rgba(24, 150, 146, 0.4),
            inset 0 1px 0 rgba(255, 255, 255, 0.2);
          
          .status-tab-icon {
            filter: brightness(0) invert(1);
          }
          
          .status-tab-text {
            color: white;
            font-weight: 600;
          }
        }
        
        .status-tab-icon {
          font-size: 14px;
          margin-right: 6px;
          transition: all 0.3s ease;
        }
        
        .status-tab-text {
          font-size: 12px;
          font-weight: 500;
          color: #ffffff;
          transition: all 0.3s ease;
          white-space: nowrap;
        }
      }
    }
  }
  
  // 实时北京时间显示
  .beijing-time-display {
    position: absolute;
    top: 20px;
    right: 380px;
    z-index: 1001;
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 16px;
    background: rgba(24, 150, 146, 0.15);
    border-radius: 8px;
    box-shadow: 
      0 4px 12px rgba(24, 150, 146, 0.2),
      0 2px 4px rgba(24, 150, 146, 0.1);
    backdrop-filter: blur(10px);
    border: 1px solid rgba(24, 150, 146, 0.3);
    transition: all 0.3s ease;
    
    &:hover {
      background: rgba(24, 150, 146, 0.2);
      transform: translateY(-1px);
      box-shadow: 
        0 6px 16px rgba(24, 150, 146, 0.25),
        0 3px 6px rgba(24, 150, 146, 0.15);
    }
    
    .time-icon {
      font-size: 20px;
      line-height: 1;
    }
    
    .time-content {
      display: flex;
      flex-direction: column;
      gap: 2px;
      
      .time-label {
        font-size: 11px;
        color: rgba(255, 255, 255, 0.8);
        font-weight: 500;
        line-height: 1.2;
      }
      
      .time-value {
        font-size: 14px;
        color: #ffffff;
        font-weight: 600;
        font-family: 'Courier New', monospace;
        letter-spacing: 0.5px;
        line-height: 1.2;
      }
    }
  }
  
  
  .map-left {
    position: absolute;
    top: 20px;
    left: 20px;
    z-index: 1000;
    display: flex;
    flex-direction: column;
    gap: 20px;
    width: 350px;
    
    .monitor-panel {
      &:first-child {
        // 设备状态面板 - 较小高度，因为内容相对简单
        height: 215px;
        min-height: 215px;
      }
    }
    .timeParam{
      width: 160px;
      position: absolute;
      top: 12px;
      right: 13px;
      z-index: 1001;
    }
  }
  
  .map-right {
    position: absolute;
    top: 20px;
    right: 20px;
    z-index: 1000;
    display: flex;
    flex-direction: column;
    gap: 20px;
    width: 350px;
    
    .monitor-panel {
      // 实时数据面板 - 平均分配高度，用于显示图表
      height: calc((100vh - 200px) / 2);
      min-height: 300px;
      max-height: 500px;
    }
  }
  
  .monitor-panel {
    position: relative;
    z-index: 1000;
    pointer-events: auto;
    background: rgba(24, 150, 146, 0.15);
    border: 1px solid rgba(24, 150, 146, 0.3);
    border-radius: 12px;
    backdrop-filter: blur(8px);
    box-shadow: 
      0 4px 12px rgba(24, 150, 146, 0.1),
      0 2px 4px rgba(24, 150, 146, 0.05),
      inset 0 1px 0 rgba(255, 255, 255, 0.1);
    transition: all 0.3s ease;
    
    &:hover {
      background: rgba(24, 150, 146, 0.2);
      border-color: rgba(24, 150, 146, 0.4);
      box-shadow: 
        0 6px 16px rgba(24, 150, 146, 0.15),
        0 3px 6px rgba(24, 150, 146, 0.08),
        inset 0 1px 0 rgba(255, 255, 255, 0.15);
      transform: translateY(-1px);
    }
  }
  
  // 风速图例样式
  .wind-legend {
    position: absolute;
    bottom: 4px;
    right: 376px;
    z-index: 1000;
    background: linear-gradient(135deg, rgba(255, 255, 255, 0.95) 0%, rgba(255, 255, 255, 0.98) 100%);
    border: 1px solid rgba(200, 200, 210, 0.5);
    border-radius: 12px;
    padding: 10px 12px;
    box-shadow: 
      0 6px 15px rgba(0, 0, 0, 0.15),
      0 2px 4px rgba(0, 0, 0, 0.1),
      inset 0 1px 0 rgba(255, 255, 255, 0.8);
    min-width: 110px;
    backdrop-filter: blur(8px);
    transition: all 0.3s ease;
    
    &:hover {
      transform: translateY(-2px);
      box-shadow: 
        0 15px 35px rgba(0, 0, 0, 0.2),
        0 6px 10px rgba(0, 0, 0, 0.15),
        inset 0 1px 0 rgba(255, 255, 255, 0.9);
    }
    
    .legend-title {
      font-size: 13px;
      font-weight: 600;
      color: #000000;
      margin-bottom: 8px;
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 8px;
      
      .legend-config-btn {
        background: transparent;
        color: #000000;
        border: none;
        padding: 0;
        cursor: pointer;
        font-size: 14px;
        line-height: 1;
        opacity: 0.7;
        transition: opacity .2s ease;
      }
      .legend-config-btn:hover { opacity: 1; }
      
      .legend-toggle-btn {
        background: transparent;
        border: 1px solid rgba(0, 0, 0, 0.2);
        border-radius: 4px;
        padding: 2px 6px;
        cursor: pointer;
        font-size: 14px;
        line-height: 1;
        transition: all 0.2s ease;
        opacity: 0.7;
        
        &:hover {
          opacity: 1;
          background: rgba(24, 150, 146, 0.1);
          border-color: rgba(24, 150, 146, 0.4);
          transform: scale(1.1);
        }
        
        &:active {
          transform: scale(0.95);
        }
      }
    }
    
    .legend-item {
      display: flex;
      align-items: center;
      margin-bottom: 6px;
      border-radius: 6px;
      transition: all 0.2s ease;
      position: relative;
      
      &:hover {
        background: rgba(24, 150, 146, 0.15);
        transform: translateX(2px);
      }
      
      &:last-child {
        margin-bottom: 0;
      }
      
      .wind-barb-demo {
        width: 24px;
        height: 24px;
        margin-right: 8px;
        flex-shrink: 0;
        background-size: contain;
        background-repeat: no-repeat;
        background-position: center;
        border-radius: 3px;
        box-shadow: 0 1px 3px rgba(24, 150, 146, 0.2);
        transition: transform 0.2s ease;
        
        &:hover {
          transform: scale(1.1);
        }
      }
      
      .wind-barb-img,
      .wind-barb-canvas {
        width: 24px;
        height: 24px;
        margin-right: 8px;
        flex-shrink: 0;
        object-fit: contain;
        border-radius: 3px;
        box-shadow: 0 1px 3px rgba(24, 150, 146, 0.2);
        transition: transform 0.2s ease;
        
        &:hover {
          transform: scale(1.1);
        }
      }
      
      .legend-text {
        font-size: 11px;
        font-weight: 500;
        color: #000000;
        white-space: nowrap;
        letter-spacing: 0.025em;
      }
    }

    .legend-editor {
      margin-top: 8px;
      padding: 8px;
      background: rgba(24, 150, 146, 0.1);
      border: 1px dashed rgba(24, 150, 146, 0.3);
      border-radius: 8px;
      
      .legend-editor-row {
        display: flex;
        align-items: center;
        gap: 6px;
        flex-wrap: wrap;
        .legend-editor-label { color: #ffffff; font-size: 12px; }
        .legend-editor-fixed { color: #ffffff; font-size: 12px; }
        span { color: #ffffff; font-size: 12px; }
        .legend-input {
          width: 64px;
          padding: 2px 6px;
          border-radius: 4px;
          border: 1px solid rgba(24, 150, 146, 0.4);
          background: rgba(24, 150, 146, 0.1);
          color: #ffffff;
          font-size: 12px;
          
          &:focus {
            outline: none;
            border-color: rgba(24, 150, 146, 0.6);
            background: rgba(24, 150, 146, 0.2);
          }
        }
        .legend-save-btn {
          background: rgba(255, 255, 255, 0.8);
          color: #ffffff;
          border: 1px solid rgba(24, 150, 146, 0.6);
          border-radius: 4px;
          padding: 4px 10px;
          cursor: pointer;
          font-size: 12px;
          transition: all 0.2s ease;
          
          &:hover {
            background: rgba(24, 150, 146, 1);
            border-color: rgba(255, 255, 255, 0.8);
          }
        }
      }
      .legend-editor-tip { color: #ffffff; margin-top: 6px; font-size: 12px; }
    }
  }
  
  // 热力图图例样式
  .heatmap-legend {
    position: absolute;
    bottom: -6px;
    right: 376px;
    z-index: 1000;
    background: linear-gradient(135deg, rgba(24, 150, 146, 0.15) 0%, rgba(24, 150, 146, 0.1) 100%);
    border: 1px solid rgba(24, 150, 146, 0.3);
    border-radius: 12px;
    padding: 10px 12px;
    box-shadow: 
      0 6px 15px rgba(24, 150, 146, 0.2),
      0 2px 4px rgba(24, 150, 146, 0.1),
      inset 0 1px 0 rgba(255, 255, 255, 0.2);
    min-width: 120px;
    backdrop-filter: blur(8px);
    transition: all 0.3s ease;
    
    &:hover {
      transform: translateY(-2px);
      box-shadow: 
        0 15px 35px rgba(24, 150, 146, 0.3),
        0 6px 10px rgba(24, 150, 146, 0.15),
        inset 0 1px 0 rgba(255, 255, 255, 0.3);
    }
    
    .legend-title {
      font-size: 13px;
      font-weight: 600;
      color: #ffffff;
      margin-bottom: 8px;
      text-align: center;
      background: linear-gradient(135deg, rgba(24, 150, 146, 0.9), rgba(24, 150, 146, 0.7));
      -webkit-background-clip: text;
      // -webkit-text-fill-color: transparent;
      background-clip: text;
      position: relative;
      
      &::after {
        content: '';
        position: absolute;
        bottom: -4px;
        left: 50%;
        transform: translateX(-50%);
        width: 20px;
        height: 1.5px;
        background: linear-gradient(90deg, rgba(24, 150, 146, 0.9), rgba(24, 150, 146, 0.7));
        border-radius: 1px;
      }
    }
    
    .legend-item {
      display: flex;
      align-items: center;
      // margin-bottom: 6px;
      padding: 2px 6px;
      border-radius: 6px;
      transition: all 0.2s ease;
      position: relative;
      
      &:hover {
        background: rgba(59, 130, 246, 0.08);
        transform: translateX(2px);
      }
      
      &:last-child {
        margin-bottom: 0;
      }
      
      .heatmap-color {
        width: 20px;
        height: 12px;
        margin-right: 8px;
        flex-shrink: 0;
        border-radius: 3px;
        box-shadow: 0 1px 3px rgba(24, 150, 146, 0.2);
        transition: transform 0.2s ease;
        
        &:hover {
          transform: scale(1.1);
        }
      }
      
      .legend-text {
        font-size: 11px;
        font-weight: 500;
        color: #ffffff;
        white-space: nowrap;
        letter-spacing: 0.025em;
      }
    }
  }
}

// 响应式设计
@media (max-width: 1400px) {
  .heatmap-page {
    .map {
      height: calc(100vh - 140px);
      min-height: 500px;
    }
    
    .map-type-switcher {
      bottom: 15px;
      left: 15px;
      
      .switcher-tabs {
        .tab-item {
          padding: 3px 6px;
          min-width: 50px;
          
          .tab-icon {
            font-size: 10px;
            margin-right: 3px;
          }
          
          .tab-text {
            font-size: 10px;
          }
        }
      }
    }
    
    .map-left,
    .map-right {
      width: 320px;
      max-height: calc(100vh - 180px);
    }
    
    .map-left {
      top: 70px;
      
      .monitor-panel {
        &:first-child {
          height: 180px;
          min-height: 180px;
        }
        &:last-child {
          height: calc(100vh - 320px);
          min-height: 350px;
          max-height: 500px;
        }
      }
    }
    
    .map-right {
      .monitor-panel {
        height: calc((100vh - 200px) / 2);
        min-height: 250px;
        max-height: 400px;
      }
    }
    
    .map-controls-group {
      bottom: 10px;
      left: 10px;
      
      .scan-speed-config {
        padding: 8px 10px;
        min-width: 180px;
        border-radius: 10px;
        
        .speed-config-title {
          font-size: 12px;
        }
        
        .speed-config-editor {
          padding: 6px;
          
          .speed-config-row {
            gap: 6px;
            
            .speed-config-input {
              width: 80px;
              padding: 3px 6px;
              font-size: 11px;
            }
            
            .speed-config-save-btn {
              padding: 3px 10px;
              font-size: 11px;
            }
          }
          
          .speed-config-tip {
            font-size: 10px;
            margin-top: 4px;
          }
        }
      }
    }

    .wind-legend,
    .heatmap-legend {
      bottom: 15px;
      right: 15px;
      padding: 8px 10px;
      min-width: 100px;
      border-radius: 10px;
      
      .legend-title {
        font-size: 12px;
        margin-bottom: 6px;
      }
      
      .legend-item {
        margin-bottom: 5px;
        padding: 3px 4px;
        
        .wind-barb-demo {
          width: 20px;
          height: 20px;
          margin-right: 6px;
        }
        
        .wind-barb-img {
          width: 20px;
          height: 20px;
          margin-right: 6px;
        }
        
        .heatmap-color {
          width: 16px;
          height: 10px;
          margin-right: 6px;
        }
        
        .legend-text {
          font-size: 10px;
        }
      }
    }
  }
}

@media (max-width: 1200px) {
  .heatmap-page {
    .map {
      height: calc(100vh - 140px);
      min-height: 400px;
    }
    
    .map-type-switcher {
      top: 10px;
      left: 10px;
      
      .switcher-tabs {
        .tab-item {
          padding: 2px 4px;
          min-width: 45px;
          
          .tab-icon {
            font-size: 9px;
            margin-right: 2px;
          }
          
          .tab-text {
            font-size: 9px;
          }
        }
      }
    }
    
    .map-left,
    .map-right {
      width: 300px;
      max-height: calc(100vh - 180px);
    }
    
    .map-left {
      top: 60px;
      
      .monitor-panel {
        &:first-child {
          height: 160px;
          min-height: 160px;
        }
        &:last-child {
          height: calc(100vh - 280px);
          min-height: 300px;
          max-height: 450px;
        }
      }
    }
    
    .map-right {
      .monitor-panel {
        height: calc((100vh - 180px) / 2);
        min-height: 200px;
        max-height: 350px;
      }
    }
    
    .map-controls-group {
      bottom: 10px;
      left: 10px;
      
      .scan-speed-config {
        padding: 6px 8px;
        min-width: 160px;
        border-radius: 8px;
        
        .speed-config-title {
          font-size: 11px;
        }
        
        .speed-config-editor {
          padding: 5px;
          
          .speed-config-row {
            gap: 5px;
            
            .speed-config-label {
              font-size: 11px;
            }
            
            .speed-config-input {
              width: 70px;
              padding: 2px 5px;
              font-size: 10px;
            }
            
            .speed-config-save-btn {
              padding: 2px 8px;
              font-size: 10px;
            }
          }
          
          .speed-config-tip {
            font-size: 9px;
            margin-top: 3px;
          }
        }
      }
    }

    .wind-legend,
    .heatmap-legend {
      bottom: 10px;
      right: 10px;
      padding: 6px 8px;
      min-width: 90px;
      border-radius: 8px;
      
      .legend-title {
        font-size: 11px;
        margin-bottom: 5px;
        
        &::after {
          width: 15px;
          height: 1px;
        }
      }
      
      .legend-item {
        margin-bottom: 4px;
        padding: 2px 3px;
        
        .wind-barb-demo {
          width: 18px;
          height: 18px;
          margin-right: 5px;
        }
        
        .wind-barb-img {
          width: 18px;
          height: 18px;
          margin-right: 5px;
        }
        
        .heatmap-color {
          width: 14px;
          height: 8px;
          margin-right: 5px;
        }
        
        .legend-text {
          font-size: 9px;
        }
      }
    }
  }
}

@media (max-width: 768px) {
  .heatmap-page {
    .map {
      height: calc(100vh - 140px);
      min-height: 300px;
    }
    
    .map-left,
    .map-right {
      position: relative;
      top: auto;
      left: auto;
      right: auto;
      margin: 10px;
      max-height: none;
      width: 100%;
      max-width: none;
    }
    
    .monitor-panel {
      position: relative;
      margin: 10px;
     }
   }
   
   // 粒子图图例样式
   .particle-legend {
     position: absolute;
     bottom: -6px;
     right: 376px;
     z-index: 1000;
     background: linear-gradient(135deg, rgba(255, 255, 255, 0.98) 0%, rgba(248, 250, 252, 0.95) 100%);
     border: 1px solid rgba(59, 130, 246, 0.2);
     border-radius: 12px;
     padding: 10px 12px;
     box-shadow: 
       0 6px 15px rgba(0, 0, 0, 0.08),
       0 2px 4px rgba(0, 0, 0, 0.04),
       inset 0 1px 0 rgba(255, 255, 255, 0.8);
     min-width: 120px;
     backdrop-filter: blur(8px);
     transition: all 0.3s ease;
     
     &:hover {
       transform: translateY(-2px);
       box-shadow: 
         0 15px 35px rgba(0, 0, 0, 0.15),
         0 6px 10px rgba(0, 0, 0, 0.08),
         inset 0 1px 0 rgba(255, 255, 255, 0.9);
     }
     
     .legend-title {
       font-size: 13px;
       font-weight: 600;
       color: #1e293b;
       margin-bottom: 8px;
       text-align: center;
       background: linear-gradient(135deg, #3b82f6, #1d4ed8);
       -webkit-background-clip: text;
       -webkit-text-fill-color: transparent;
       background-clip: text;
       position: relative;
       
       &::after {
         content: '';
         position: absolute;
         bottom: -4px;
         left: 50%;
         transform: translateX(-50%);
         width: 20px;
         height: 1.5px;
         background: linear-gradient(90deg, #3b82f6, #1d4ed8);
         border-radius: 1px;
       }
     }
     
     .legend-item {
       display: flex;
       align-items: center;
       margin-bottom: 6px;
       padding: 4px 6px;
       border-radius: 6px;
       transition: all 0.2s ease;
       position: relative;
       
       &:hover {
         background: rgba(59, 130, 246, 0.08);
         transform: translateX(2px);
       }
       
       &:last-child {
         margin-bottom: 0;
       }
       
       .wind-demo {
         width: 8px;
         height: 8px;
         margin-right: 8px;
         flex-shrink: 0;
         border-radius: 50%;
         box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
         transition: transform 0.2s ease;
         animation: windDemo 2s linear infinite;
         
         &:hover {
           transform: scale(1.1);
         }
       }
       
       .legend-text {
         font-size: 11px;
         font-weight: 500;
         color: #475569;
         white-space: nowrap;
         letter-spacing: 0.025em;
       }
     }
   }
 }
</style>

<style lang="scss">
// 风向标记飘动动画（全局样式）
@keyframes windFloat {
  0%, 100% {
    transform: translateY(0px) rotate(var(--wind-direction, 0deg));
  }
  25% {
    transform: translateY(-2px) rotate(var(--wind-direction, 0deg));
  }
  50% {
    transform: translateY(-4px) rotate(var(--wind-direction, 0deg));
  }
  75% {
    transform: translateY(-2px) rotate(var(--wind-direction, 0deg));
  }
}

// 风向标记样式（全局样式）
.floating-wind-marker {
  transition: all 0.3s ease;
  
  &:hover {
    transform: scale(1.1) rotate(var(--wind-direction, 0deg)) !important;
    filter: brightness(1.2);
  }
}

// 粒子动画（全局样式）
@keyframes particleFloat {
  0%, 100% {
    transform: translateY(0px) scale(1);
    opacity: 1;
  }
  25% {
    transform: translateY(-3px) scale(1.1);
    opacity: 0.8;
  }
  50% {
    transform: translateY(-6px) scale(1.2);
    opacity: 0.6;
  }
  75% {
    transform: translateY(-3px) scale(1.1);
    opacity: 0.8;
  }
}

// 风场演示动画
@keyframes windDemo {
  0% {
    transform: rotate(0deg) scale(1);
    opacity: 1;
  }
  50% {
    transform: rotate(180deg) scale(1.1);
    opacity: 0.8;
  }
  100% {
    transform: rotate(360deg) scale(1);
    opacity: 1;
  }
}

// 风场旋转动画（增强版）
@keyframes windRotate {
  0% {
    transform: rotate(0deg) scale(1);
  }
  25% {
    transform: rotate(90deg) scale(1.1);
  }
  50% {
    transform: rotate(180deg) scale(1.2);
  }
  75% {
    transform: rotate(270deg) scale(1.1);
  }
  100% {
    transform: rotate(360deg) scale(1);
  }
}

// 下雨动画
@keyframes rainFall {
  0% {
    transform: translateY(-100vh) rotate(0deg);
    opacity: 0;
  }
  10% {
    opacity: 1;
  }
  90% {
    opacity: 1;
  }
  100% {
    transform: translateY(100vh) rotate(5deg);
    opacity: 0;
  }
}

// 风场粒子样式
.wind-field-particle {
  transition: all 0.1s ease;
  will-change: transform, opacity, left, top;
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
  
  // 添加发光效果
  filter: drop-shadow(0 0 3px currentColor);
  
  &:hover {
    transform: scale(1.5) !important;
    filter: brightness(1.8) drop-shadow(0 0 6px currentColor);
  }
}

// 粒子图右下角图例样式 - 完全参考热力图图例
.particle-corner-legend {
  position: absolute;
  bottom: -6px;
  right: 376px;
  z-index: 1000;
  background: linear-gradient(135deg, rgba(255, 255, 255, 0.98) 0%, rgba(248, 250, 252, 0.95) 100%);
  border: 1px solid rgba(59, 130, 246, 0.2);
  border-radius: 12px;
  padding: 10px 12px;
  box-shadow: 
    0 6px 15px rgba(0, 0, 0, 0.08),
    0 2px 4px rgba(0, 0, 0, 0.04),
    inset 0 1px 0 rgba(255, 255, 255, 0.8);
  min-width: 120px;
  backdrop-filter: blur(8px);
  transition: all 0.3s ease;
  
  &:hover {
    transform: translateY(-2px);
    box-shadow: 
      0 15px 35px rgba(0, 0, 0, 0.15),
      0 5px 15px rgba(0, 0, 0, 0.1),
      inset 0 1px 0 rgba(255, 255, 255, 0.9);
  }
  
  .corner-legend-title {
    font-size: 13px;
    font-weight: 600;
    color: #1e293b;
    margin-bottom: 8px;
    text-align: center;
    position: relative;
    
    &::after {
      content: '';
      position: absolute;
      bottom: -4px;
      left: 50%;
      transform: translateX(-50%);
      width: 20px;
      height: 2px;
      background: linear-gradient(90deg, #3b82f6, #8b5cf6);
      border-radius: 1px;
    }
  }
  
  .corner-legend-content {
    display: flex;
    flex-direction: column;
    gap: 6px;
  }
  
  .corner-legend-item {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 5px;
    padding: 3px 4px;
    border-radius: 6px;
    transition: all 0.2s ease;
    
    &:hover {
      background: rgba(59, 130, 246, 0.05);
      transform: translateX(2px);
    }
    
    .corner-wind-demo {
      width: 12px;
      height: 12px;
      border-radius: 50%;
      flex-shrink: 0;
      box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
      animation: windDemo 2s linear infinite;
    }
    
    .corner-legend-text {
      font-size: 11px;
      font-weight: 500;
      color: #475569;
      line-height: 1.3;
    }
  }
  
  // 响应式设计 - 参考热力图图例
  @media (max-width: 1024px) {
    bottom: 15px;
    right: 15px;
    padding: 8px 10px;
    min-width: 100px;
    border-radius: 10px;
    
    .corner-legend-title {
      font-size: 12px;
      margin-bottom: 6px;
    }
    
    .corner-legend-item {
      margin-bottom: 5px;
      padding: 3px 4px;
      
      .corner-wind-demo {
        width: 10px;
        height: 10px;
      }
      
      .corner-legend-text {
        font-size: 10px;
      }
    }
  }
  
  @media (max-width: 768px) {
    bottom: 10px;
    right: 10px;
    padding: 6px 8px;
    min-width: 90px;
    border-radius: 8px;
    
    .corner-legend-title {
      font-size: 11px;
      margin-bottom: 5px;
      
      &::after {
        width: 15px;
        height: 1px;
      }
    }
    
    .corner-legend-item {
      margin-bottom: 4px;
      padding: 2px 3px;
      
      .corner-wind-demo {
        width: 8px;
        height: 8px;
      }
      
      .corner-legend-text {
        font-size: 9px;
      }
    }
  }
}

// 风场流动动画
@keyframes windFlow {
  0% {
    transform: translateX(0) translateY(0) scale(1);
    opacity: 0.8;
  }
  25% {
    transform: translateX(2px) translateY(-1px) scale(1.1);
    opacity: 1;
  }
  50% {
    transform: translateX(4px) translateY(-2px) scale(1.2);
    opacity: 0.9;
  }
  75% {
    transform: translateX(2px) translateY(-3px) scale(1.1);
    opacity: 1;
  }
  100% {
    transform: translateX(0) translateY(-4px) scale(1);
    opacity: 0.8;
  }
}

// 粒子轨迹效果
.wind-flow-particle::before {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 200%;
  height: 200%;
  background: radial-gradient(circle, rgba(255, 255, 255, 0.1) 0%, transparent 70%);
  border-radius: 50%;
  transform: translate(-50%, -50%);
  animation: particleTrail 2s ease-in-out infinite;
  pointer-events: none;
  z-index: -1;
}

@keyframes particleTrail {
  0%, 100% {
    opacity: 0.3;
    transform: translate(-50%, -50%) scale(1);
  }
  50% {
    opacity: 0.6;
    transform: translate(-50%, -50%) scale(1.5);
  }
}

// 雷达波动画
@keyframes radarPulse {
  0% {
    transform: scale(0.1);
    opacity: 0.8;
  }
  50% {
    transform: scale(0.8);
    opacity: 0.4;
  }
  100% {
    transform: scale(1.2);
    opacity: 0;
  }
}

// 雷达波样式
.radar-overlay {
  pointer-events: none;
  z-index: 999;
}

.radar-circle,
.radar-circle-2,
.radar-circle-3 {
  position: absolute;
  border-radius: 50%;
  box-shadow: 0 0 10px rgba(0, 255, 0, 0.5);
}

// 风羽图扫描线样式
.satellite-scan-overlay {
  pointer-events: none;
  z-index: 998;
  opacity: 0.8;
  transition: opacity 0.3s ease;
  
  &:hover {
    opacity: 1;
  }
}

// 加载动画
@keyframes spin {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}
</style>
